For this post, I talk about mocking objects in Python. As a Rubyist, this one was initially confusing especially since I was not yet familiar with Python’s package import magic.
You need to understand this part before you can effectively mock objects in Python.
Say you have a file named
my_package1.py with the following code:
1 2 3 4 class A(object): def __init__(self): ...
Now say you want to import that in
my_package2.py as follows:
1 2 3 4 5 6 from my_package1 import A class B(object): def __init__(self): ...
What that first line in
my_package2.py effectively does is create a variable
A under the
my_package2 namespace that points to the actual
in memory. So now we have two variables pointing to the same memory address
and their fully qualified names are
my_package1.A --| |---> <Actual A Class> my_package2.A --|
Now let’s say in
my_package2, you use an unqualified
A like the following:
1 2 3 4 class B(object): def __init__(self): self.a = A()
Behind the scenes, the interpreter will attempt to find an
A variable in the
my_package2 namespace, find it there and use that to get to the class in
memory. So the code inside
my_package2.py is effectively using the
Now we’re ready to mock objects.
Once you understand how importing and namespacing in Python works, the rest
(well, most of it) is straightforward. Let’s say you want to test
B so you
then write a test for it as follows:
1 2 3 4 5 6 from my_package2 import B class TestB: def test_initialization(self): subject = B()
Now let’s say we want to focus our test on
B and just mock out
A. What we need
to do is mock out
my_package2.A since that’s what all unqualified
resolve to inside
my_package2. To mock out that variable, we use the patch
1 2 3 4 5 6 7 8 from mock import patch from my_package2 import B class TestB: @patch('my_package2.A') def test_initialization(self, mock_A): subject = B()
There’s a lot happening in the above code so let’s break it down:
from mock import patchmakes the
patchdecorator available in this namespace
patchdecorator to replace the memory address that
my_package2.Apoints to. This new memory address contains an instance of the
patchdecorator is that our
test_initializationmethod will receive an extra parameter which is a reference to the same
Mockinstance created in the previous step. This is why we have an additional parameter
Reusing our diagram from above, the end result is this:
my_package1.A ------> <Actual A Class> my_package2.A --| |---> <Mock object pretending to be the A class. Impostor!> mock_A ---------|
test_initialization, you can use the
mock_A variable to describe
how the mocked
A should behave and you can even make assertions on it. For
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from mock import patch from my_package2 import B class TestB: @patch('my_package2.A') def test_initialization(self, mock_A): # Mock A's do_something method mock_A.do_something.return_value = True subject = B() # Check if B called A.do_something() correctly mock_A.do_something.assert_called_once_with('foo')
Go read more about Mock in this extensive online reference. Then come back for some tips on mocking attributes vs. methods.
If you want to mock an attribute or property, and you don’t care about how many times it was called (usually, you don’t), just mock it like so:
1 mock_A.some_attribute = 'somevalue'
When the subject under test calls mock_A’s supposed constructor, it will
return another Mock object. This new Mock object poses as an instance
of the mocked A class. If you want to make changes to this mocked instance,
mock_A’s return value:
1 2 3 4 5 6 mock_instance = mock_A.return_value mock_instance.say_hello.return_value = "hello!" subject = B() # Make assertions on mock_instance and mock_A here
But what if you want the mocked method to return “hello” on the first call and then “olleh!” in the second call (assuming the code under test calls it twice). You can mock it like so:
1 mock_instance.say_hello.side_effect = ["hello!", "olleh!"]
Note how we’re using
side_effect here instead of
return_value. You can also
assign a function to
side_effect but so far I’ve been able to avoid that
complication by just using a list of return values.
One of the gotchas of mocking is that you might end up with behavior that’s
specified in the mock object, but not really implemented in the real object.
The result is that you have unit tests that pass but integration tests that
fail. This is easily fixed but prevention is way better. That’s why I make use
1 2 3 4 5 6 7 8 9 10 11 12 from mock import patch from my_package2 import B class TestB: @patch('my_package2.A', autospec=True) def test_initialization(self, mock_A): # Mock A here subject = B() # Check calls to A here
The effect here is that mock_A will have the same signature (methods, properties, etc) as the actual A class and you can’t mock any attributes on mock_A that isn’t already defined in the actual class.
Remember that you can interrogate the mock objects and make assertions on how many times their methods were called and even how they were called. Read more about that here.
That’s it for now. Live long and prosper in your tests!