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/.
import mock
Here I use a mock object to stand in for a function. No matter how it is called it will return 42
.
mock_func = mock.Mock()
mock_func.return_value = 42
print mock_func(6, 9)
Mock objects remember how they have been called and you can test that they were called correctly.
mock_func.assert_called_with(6, 9)
If the calling sequences don't match you get an assertion error.
mock_func.assert_called_with(6, 7)
assert_called_with
applies only to the most recent call.
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.
mock_class = mock.NonCallableMock()
mock_class.some_method.return_value = 42
print mock_class.some_method(6, 9)
mock_class.some_method.assert_called_once_with(6, 9)
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.
mock_func_w_side_effect = mock.Mock()
mock_func_w_side_effect.side_effect = ValueError('Wrong!')
mock_func_w_side_effect()
Another side effect is a function that actually does something, but I couldn't think of many uses for this.
mock_func_w_side_effect.side_effect = lambda x, y: x + y
mock_func_w_side_effect('spam', 'SPAM')
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.)
import json
def func_with_json(d):
return json.dumps(d)
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.
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:
print json.dumps(d)
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.
@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()
There are a number of different kinds of patches and different ways to use them. For more information refer to the mock documentation.