relaxdiego (Mark Maglana's Technical Blog)

Mocking Objects in Python

Apr 7, 2014
Est. read: 8 minutes

Mocking in Python can be initially confusing and the official docs, while informative, don’t make learning it any easier for newcomers. In my case, I came across it when I was still transitioning from Ruby which, I believe, contributed to the confusion. This article takes a gentler approach to learning Python’s mocking library to help you get productive sooner.

Importing Objects: A Review

The unittest.mock library builds on top of how Python implements the import statement so it’s imperative that we have a solid understanding of how it works before we can continue.

Let’s say we have a file named module1.py with the following code:

1
2
3
4
5
6
# module1.py

class A(object):

    def __init__(self):
      ...

And let’s also say we have a file named module2.py with the following code:

1
2
3
4
5
6
7
8
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        ...

If you look at module2.py line 3, what it’s effectively doing is that it’s creating a new variable named A that is local to module2 and this variable points to the actual A class in memory as defined by module1.

What this means is that we now have two variables pointing to the same memory address. These two variables are named module1.A and module2.A:

               |------------------|
module1.A ---> |                  |
               | <Actual A Class> |
module2.A ---> |                  |
               |------------------|

Now let’s say that, in module2, we used an unqualified A as in line 8 of the following:

1
2
3
4
5
6
7
8
# module2.py

from module1 import A

class B(object):

    def __init__(self):
        self.a = A()

Behind the scenes, Python will try to find an A variable in the module2 namespace. After finding the variable, it then uses it to get to the actual A class in memory. Effectively, the A() in line 8 above is shorthand for module2.A().

Mocking Objects

Building on top of what we learned in the previous section, let’s say we want to test our class B from above. We would then write our test initially as follows:

1
2
3
4
5
6
7
8
# test_module2.py

from module2 import B

class TestB:

    def test_initialization(self):
        subject = B()

Now let’s say we want to focus our test on the logic of B and not care about the internals of A for now. To do that, we need to mock out the variable module2.A since that’s what all unqualified A’s in module2 will resolve to.

To mock out all unqualified A’s in module2, we use the patch decorator as in line 8 below:

1
2
3
4
5
6
7
8
9
10
# test_module2.py

from mock import patch
from module2 import B

class TestB:

    @patch('module2.A')
    def test_initialization(self, mock_A):
        subject = B()

There’s a lot happening above so let’s break it down:

  1. Line 3: from mock import patch makes the patch decorator available to our tests.
  2. Line 8: Using the patch decorator, we create an instance of Mock and make the variable module2.A point to this instance.

Reusing our diagram from above, our new reality is as follows:

               |------------------|
module1.A ---> | <Actual A Class> |
               |------------------|

               |------------------|
module2.A ---> |      <Mock>      |
               |------------------|

Our use of the patch decorator above has another side effect. Namely that this decorator provides our test method with a reference to the Mock instance it created. We capture this reference using the mock_A parameter in line 9.

So a more complete representation of our new reality is as follows:

               |------------------|
module1.A ---> | <Actual A Class> |
               |------------------|

               |------------------|
module2.A ---> |                  |
               |      <Mock>      |
   mock_A ---> |                  |
               |------------------|

Note that since we are patching module2.A via the decorator method, this new reality is effective within the context of the test_initialization() method only.

With that, we can now use mock_A to describe how our mock A class should behave:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# test_module2.py

from mock import patch
from module2 import B

class TestB:

    @patch('module2.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')

And that’s it! You now have the basics of mocking in Python. To dive deeper, visit the official documentation for unittest.mock.

Tips on Mocking Attributes and Methods

Stubbing Attributes

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 stub it like so:

1
mock_A.some_attribute = 'somevalue'

Mocking Instances

When the subject under test (SUT) attempts to instantiate an object from our Mock A class above, another Mock object is created and returned. This new Mock object pretends to be an instance of A. If you want to customize how this mock instance of A behaves, first get a reference to it via the return_value attribute:

1
2
3
4
5
6
7
8
9
10
11
# Get a reference to the mock A instance
mock_instance = mock_A.return_value

# Now customize its behavior
mock_instance.say_hello.return_value = "hello!"

# Exercise the SUT
subject = B()

# Make assertions against the mock instance
mock_instance.say_hello.assert_called_once_with('foo')

Returning Different Values

Now what if you want the mocked method to return “hello” on the first call and then “olleh!” in the second call (assuming the SUT calls it twice). You can mock it like so:

1
2
3
4
5
6
7
# Get a reference to the mock A instance
mock_instance = mock_A.return_value

# Now customize its behavior
mock_instance.say_hello.side_effect = ["hello!", "olleh!"]

...

Note how we’re using side_effect in line 5 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.

Avoiding Phantom Mocks

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 code that passes the tests but fails in production. You can prevent this from happening by setting the autospec and spec_set parameters in the patch decorator as in line 8 below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# test_module2.py

from mock import patch
from module2 import B

class TestB:

    @patch('module2.A', autospec=True, spec_set=True)
    def test_initialization(self, mock_A):
        # Mock A here

        subject = B()

        # Check calls to A here

By using autospec above, we are automatically defining the Mock oject with the same specs as the actual module1.A class. Likewise, by using spec_set above, we are “freezing” the specs of the Mock object such that we don’t accidentally create phantom mock attributes or methods in it.

That’s it for now. If you want to dive deeper into the internals of Python’s mocking library, head on over to the official documentation for unittest.mock.