Patching imported modules

New in version 0.6.

With Mockify you can easily substitute imported module with a mocked version. Consider following code:

import os

def iter_dirs(path):
    for name in os.listdir(path):
        fullname = os.path.join(path, name)
        if os.path.isdir(fullname):
            yield fullname

That function generates full paths to all direct children directories of given path. And it uses os to make some file system operations. To test that function without refactoring it you will have to patch some methods of os module. And here’s how this can be done in Mockify:

from mockify.core import satisfied, patched
from mockify.mock import Mock
from mockify.actions import Return

def test_iter_dirs():
    os = Mock('os')  # (1)
    os.listdir.expect_call('/tmp').will_once(Return(['foo', 'bar', 'baz.txt']))  # (2)
    os.path.isdir.expect_call('/tmp/foo').will_once(Return(True))  # (3)
    os.path.isdir.expect_call('/tmp/bar').will_once(Return(True))
    os.path.isdir.expect_call('/tmp/baz.txt').will_once(Return(False))

    with patched(os):  # (4)
        with satisfied(os):  # (5)
            assert list(iter_dirs('/tmp')) == ['/tmp/foo', '/tmp/bar']  # (6)

And here’s what’s going on in presented test:

  • We’ve created os mock (1) for mocking os.listdir() (2) and os.path.isdir() (3) methods,
  • Then we’ve used mockify.core.patched() context manager (4) that does the whole magic of substituting modules matching full names of mocks with expectations recorded (which are 'os.listdir' and 'os.path.isdir' in our case) with corresponding mock objects
  • Finally, we’ve used mockify.core.satisfied() context manager (5) to ensure that all expectations are satisfied, and ran tested function (6) checking it’s result.

Note that we did not mock os.path.join() - that will be used from os module.