14 February 2017

Modules that are patched with mock’s patch decorator must be re-imported. This is a simple thing but I wasted a couple of hours this morning on it, so I’m documenting this for future reference.

Suppose you have two files:

parent.py

class MyParent(object):
    parent_name = "me"

child.py:

from parent import MyParent


class MyChild(MyParent):
    pass

And you would like to run some tests, one where the MyParent object is patched, and one where it is not. If you use this code for your tests:

from mock import patch


class MockParent(object):
    parent_name = "someone else"


def test_child():
    import child
    assert child.MyChild().parent_name == "me"


@patch("parent.MyParent", new=MockParent)
def test_mock():
    import child
    assert child.MyChild().parent_name == "someone else"

If you run these tests, only one test will work, depending on the order. I had assumed that imports were local to the scope of the function, but it appears that they aren’t. They aren’t even local to the file - if you split these tests up, only one will work, and it will be the one in the alphabetically first named file.

To run the tests as expected, you will need to re-import the child module. First, in order for this to work with both python2 and python3, import either imp or importlib

if sys.version_info >= (3, 0):
    import importlib
else:
    import imp as importlib

Then, after patching your code, re-import the patched library:

@patch("parent.MyParent", new=MockParent)
def test_mock():
    import child
    importlib.reload(child)
    assert child.MyChild().parent_name == "someone else"


blog comments powered by Disqus