Python Mock Gotchas
Published on 16 November 2012, updated on 16 November 2012, Comments
Lately I’ve had the chance to work on a fairly large code base built upon a service-oriented architecture. In order to test each service in isolation, we make extensive use of Mock to simulate services that the code under test depends on.
During this process, we noticed that each member our team seemed to be hitting the same pitfalls, which I’m going to present in this article in the hope that it might help other developers.
Patching in the wrong place
The first thing that puzzles people is when the
patch method seem to have no
Let’s consider a very simple example where you have some kind of domain model object that uses a data source module to get data and return it to the client. In a real world situation, the data source module would query a database or an external service. For the sake of simplicity, here the data source just returns hard-coded values:
# data_source.py def get_name(): return "Alice"
Person class exposes a method that fetches data from the data source:
# person.py from data_source import get_name class Person(object): def name(self): return get_name()
One might then start to write a test case that looks like this:
# test_person.py from mock import patch from person import Person # mock the get_name function @patch('data_source.get_name') # This won't work as expected! def test_name(mock_get_name): # set a return value for our mock object mock_get_name.return_value = "Bob" person = Person() name = person.name() assert name == "Bob"
Unfortunately, the example above doesn’t work as expected. If you run it with a testing tool such as nose, you’ll get a result similar to:
$ nosetests test_person.py F ====================================================================== FAIL: test_person.test_name ---------------------------------------------------------------------- [...] return func(*args, **keywargs) File "/home/al/essais/python/test_person.py", line 13, in test_name assert name == "Bob" AssertionError ---------------------------------------------------------------------- Ran 1 test in 0.002s FAILED (failures=1)
The reason why it doesn’t work is explained in the documentation in a section called Where to patch. Now go and read it if you haven’t already, I’ll wait for you right here.
The problem in the example above is that we’re changing where the name
get_name points to within the
data_source module. However the code we’re
testing doesn’t refer to
get_name within the
data_source module, it refers
to that name within the
Our patching would have worked correctly if the code under test had referred to
get_name within the
data_source module like this:
# person.py import data_source class Person(object): def name(self): return data_source.get_name()
If we want to leave our application code unchanged, we’ll have to change the
way we use Mock within our test so that it modifies where
get_name points to
from mock import patch from person import Person @patch('person.get_name') def test_name(mock_get_name): mock_get_name.return_value = "Bob" person = Person() name = person.name() assert name == "Bob"
Forgetting to patch the return_value
Another common issue is patching an object instead of patching the return value of that object. It is particularly easy to get trapped into this when patching objects deep down inside a chain of class instantiations and method calls.
Person has a relationship with
class Person(object): def __init__(self): self.pet = Pet() [... other methods ...] class Pet(object): def noise(self): return "Woof"
It might look natural to write a test like this:
@patch('person.Pet') def test_dog_noise(mock_pet): mock_pet.noise.return_value = "Meoow" person = Person() assert person.pet.noise() == "Meoow"
This won’t work because a
pet object is the result of instantiating the
class, which in Python means calling the class. We must therefore access the
noise method via the
return_value of the mocked
@patch('person.Pet') def test_dog_noise(mock_pet): # Here we need an extra `return_value` attribute in order to access the # instance of the class mock_pet.return_value.noise.return_value = "Meoow" person = Person() assert person.pet.noise() == "Meoow"
Patching decorators might also lead to some surprising behaviors. Let’s say we have a very simple decorator that just logs the result of decorated functions:
# decorators.py def noise_logger(func): def wrapped(self): result = func(self) # In a real-world scenario, the decorator would access an external # resource which we don't want our tests to depend on, such as a # caching service. print "Pet made noise: ", result return result return wrapped
Our domain model makes use of that decorator:
# person.py from decorators import noise_logger class Person(object): def __init__(self): self.pet = Pet() class Pet(object): @noise_logger def noise(self): return "Woof"
Now we’d like to patch our decorator to make sure it doesn’t call any external service we want to isolate from. Intuitively, we might write a test like this:
from mock import patch from person import Person @patch('person.noise_logger', lambda x: x) def test_decorator(): person = Person() assert person.pet.noise() == "Woof"
Unfortunately this won’t work at all because by the time we patch our
decorator, it has already been applied and our
noise method has already been
wrapped. Here is what happens if we run our test with the
-s switch, which
tells nose to print stdout output:
$ nosetests -s test_person.py Pet made noise: Woof . ---------------------------------------------------------------------- Ran 1 test in 0.001s OK
This is because the decorator has been applied when the class
definition was loaded, as soon as we imported the
person module. Remember
that instructions within a class definition are typically interpreted when we
load the module containing the class. Consider this:
def useless_decorator(func): print "Hi, I'm a decorator that does nothing." return func class Foo(object): print "Entering Foo class definition" @useless_decorator def bar(self): return 42 print "OK, we're done with that class definition."
If you execute this code with your Python interpreter, you should get this output:
Entering Foo class definition Hi, I'm a decorator that does nothing. OK, we're done with that class definition.
I hope this silly example convinces you that decorators are applied when the class is created, which happens straight away when we load the module containing the class.
To deal with this you first have to make sure that your decorator is defined in a module separate from the class, otherwise you’ll never get a chance to replace it with a mock before the class definition calls it. Then you need to write your test code so that it patches the decorator before it gets applied to the methods of your class:
from mock import patch patch('decorators.noise_logger', lambda x: x).start() from person import Person def test_decorator(): person = Person() assert person.pet.noise() == "Woof"
Now if you run nosetests with
-s, you shouldn’t see any message from the decorator because it’s been replaced by a
lambda that does nothing.