Python Mock Gotchas
Published on 16 November 2012, updated on 16 November 2012, Comments
Mock is a Python mocking and testing library. It has become a de facto standard and is now included in the Python standard library.
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
effect.
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"
The 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 person
module.
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
within the person
module:
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.
Let’s say Person
has a relationship with Pet
:
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 Pet
class, which in Python means calling the class. We must therefore access the
noise
method via the return_value
of the mocked Pet
class:
@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
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.