A very short introduction to the mock library

Mock objects are useful in unit testing as stand ins for other objects or functions. You might use a mock object instead of the real thing when: the real thing is expensive to create, the real thing requires online resources that might be offline, or you just want to do really fine grained testing. With mock objects you can easily control what they do and then test whether they were used as intended.

There are a number of Python mock libraries but the one discussed here is mock: http://www.voidspace.org.uk/python/mock/.

In [1]:
import mock

Mock a function

Here I use a mock object to stand in for a function. No matter how it is called it will return 42.

In [2]:
mock_func = mock.Mock()
mock_func.return_value = 42
print mock_func(6, 9)
42

Mock objects remember how they have been called and you can test that they were called correctly.

In [3]:
mock_func.assert_called_with(6, 9)

If the calling sequences don't match you get an assertion error.

In [4]:
mock_func.assert_called_with(6, 7)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-4-c5e431097d00> in <module>()
----> 1 mock_func.assert_called_with(6, 7)

/Users/mrdavis/py-lib/mock-1.0b1-py2.7.egg/mock.pyc in assert_called_with(_mock_self, *args, **kwargs)
    822         if self.call_args != (args, kwargs):
    823             msg = self._format_mock_failure_message(args, kwargs)
--> 824             raise AssertionError(msg)
    825 
    826 

AssertionError: Expected call: mock(6, 7)
Actual call: mock(6, 9)

assert_called_with applies only to the most recent call.

Mock a non-callable class

Here I use a mock object to stand in for a class and method. It looks very similar to above. A NonCallableMock is basically the same as a plain Mock except it cannot be called.

In [5]:
mock_class = mock.NonCallableMock()
mock_class.some_method.return_value = 42
print mock_class.some_method(6, 9)
42
In [6]:
mock_class.some_method.assert_called_once_with(6, 9)

Side effects

Mock objects can have side effects when called instead of simple return values. One desireable side effect might be to raise an exception to make sure your code under test responds correctly.

In [7]:
mock_func_w_side_effect = mock.Mock()
mock_func_w_side_effect.side_effect = ValueError('Wrong!')
mock_func_w_side_effect()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-7-c8ad7335dff3> in <module>()
      1 mock_func_w_side_effect = mock.Mock()
      2 mock_func_w_side_effect.side_effect = ValueError('Wrong!')
----> 3 mock_func_w_side_effect()

/Users/mrdavis/py-lib/mock-1.0b1-py2.7.egg/mock.pyc in __call__(_mock_self, *args, **kwargs)
    942         # in the signature
    943         _mock_self._mock_check_sig(*args, **kwargs)
--> 944         return _mock_self._mock_call(*args, **kwargs)
    945 
    946 

/Users/mrdavis/py-lib/mock-1.0b1-py2.7.egg/mock.pyc in _mock_call(_mock_self, *args, **kwargs)
    997         if effect is not None:
    998             if _is_exception(effect):
--> 999                 raise effect
   1000 
   1001             if not _callable(effect):

ValueError: Wrong!

Another side effect is a function that actually does something, but I couldn't think of many uses for this.

In [8]:
mock_func_w_side_effect.side_effect = lambda x, y: x + y
mock_func_w_side_effect('spam', 'SPAM')
Out[8]:
'spamSPAM'

Patching

Creating mock objects directly as in the above examples can be useful for constructing objects passed to code under test but you may also want to replace functions and objects used by the code under test. Since you don't have direct access to these you can use mock's patch utility, which comes in several flavors.

As an example I'll create a toy function to test. It simply calls json.dumps. (Read more about the json module here: http://docs.python.org/library/json.html.)

In [9]:
import json
def func_with_json(d):
    return json.dumps(d)
In [10]:
d = {'a': 1, 'b': [2, 3]}  # a simple input for func_with_json

mock.patch can be used as a context manager. Here it replaces the function json.dumps. At the end of the code block within the context manager json.dumps goes back to its normal state.

In [11]:
with mock.patch('json.dumps') as mock_dumps:
    mock_dumps.return_value = 'JSON'
    r = func_with_json(d)
    assert r == 'JSON'
    mock_dumps.assert_called_once_with(d)

Outside the context block json.dumps works as normal:

In [12]:
print json.dumps(d)
{"a": 1, "b": [2, 3]}

mock.patch can also be used as a function or class decorator, replacing an object inside the function or class.

Here we use mock.patch to replace json.dumps within a test function. The mock object replacing json.dumps is passed to the test function as an argument.

In [13]:
@mock.patch('json.dumps')
def test_func_with_json(mock_dumps):
    mock_dumps.return_value = 'JSON'
    r = func_with_json({'c': {'d': [4]}})
    assert r == 'JSON'
    mock_dumps.assert_called_once_with(d)  # whoops, we didn't pass in d, this should fail.
test_func_with_json()
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-13-0c4563c6a92d> in <module>()
      5     assert r == 'JSON'
      6     mock_dumps.assert_called_once_with(d)  # whoops, we didn't pass in d, this should fail.
----> 7 test_func_with_json()

/Users/mrdavis/py-lib/mock-1.0b1-py2.7.egg/mock.pyc in patched(*args, **keywargs)
   1188 
   1189                     args += tuple(extra_args)
-> 1190                     return func(*args, **keywargs)
   1191                 except:
   1192                     if (patching not in entered_patchers and

<ipython-input-13-0c4563c6a92d> in test_func_with_json(mock_dumps)
      4     r = func_with_json({'c': {'d': [4]}})
      5     assert r == 'JSON'
----> 6     mock_dumps.assert_called_once_with(d)  # whoops, we didn't pass in d, this should fail.
      7 test_func_with_json()

/Users/mrdavis/py-lib/mock-1.0b1-py2.7.egg/mock.pyc in assert_called_once_with(_mock_self, *args, **kwargs)
    833                    self.call_count)
    834             raise AssertionError(msg)
--> 835         return self.assert_called_with(*args, **kwargs)
    836 
    837 

/Users/mrdavis/py-lib/mock-1.0b1-py2.7.egg/mock.pyc in assert_called_with(_mock_self, *args, **kwargs)
    822         if self.call_args != (args, kwargs):
    823             msg = self._format_mock_failure_message(args, kwargs)
--> 824             raise AssertionError(msg)
    825 
    826 

AssertionError: Expected call: dumps({'a': 1, 'b': [2, 3]})
Actual call: dumps({'c': {'d': [4]}})

There are a number of different kinds of patches and different ways to use them. For more information refer to the mock documentation.

In [ ]: