Along with factories, parametrize and fixtures, one more thing that you’ll see a lot of in Hypothesis’s Python tests that you probably won’t know from non-test Python code are these things called mocks, which we’ll look at in this post.
Mock
“Mock objects” are “are simulated objects that mimic the behavior of real objects in controlled ways”.
Sometimes the function or method that you’re trying to test requires an object
from another class, but you don’t want to use a real object of that other class in
your test.
For example you could be writing a test for the foo()
function and it
requires a Bar
object as argument:
def foo(bar):
...
Your test will need a Bar
object to pass foo()
, but there can be several
reasons why you wouldn’t want to use a real Bar
object in your test:
-
Bar
objects might be complicated or difficult to setup, and this would complicate the tests.This goes beyond simply instantiating
Bar
objects. Different tests will require different behaviors from theBar
object, return values and side effects such as raising exceptions, and the object needs to be setup as needed for each test. -
Bar
objects might be slow to create or to use, perhaps because they access the filesystem, and using lots of them in the tests forfoo()
would make the tests slow. -
Bar
objects might have side effects that you don’t want to happen when you run your tests, sending emails for example. -
Using the real
Bar
class in tests forfoo()
can harm test isolation - bugs inBar
could cause the tests forfoo()
(and any other tests that use the realBar
) to fail. This can create a cascade of test failures that can make the original bug hard to find. Ideally, bugs inBar
would only causeBar
‘s own tests to fail.Another problem with a lack of test isolation is that changes to
Bar
might also require changes to be made to all the tests that useBar
as well, if these tests contain code that instantiates and sets upBar
objects. -
You might want to assert things about how
foo()
uses theBar
object - how many times it calls it, what arguments it passes to it, what it does with the return values, etc.
Instead of using the real Bar
class you can create a MockBar
class for the
tests to use:
def foo(bar):
...
# Tests:
class MockBar(object):
...
def test_foo():
mock_bar = MockBar()
foo(mock_bar)
assert ...
The MockBar
class would simulate the methods of the real Bar
class -
MockBar
would have the same methods, they’d take the same arguments, and
they’d return whatever values the test wants them to return to foo()
(for
example, these might just be fake methods that return hard-coded values that
look like what the real Bar
class might return).
But MockBar
objects would be easy for tests to create, fast, have no
real-world side effects, and not break if the real Bar
class has bugs.
MockBar
could also keep a record of what foo()
does with it - what methods
it calls and what arguments it passes to them. The test’s assert
statements
can query this record to test things about how foo()
uses MockBar
.
For more on mocks see wikipedia, Martin Fowler and wiki.c2.com.
Many programming languages come with a way of doing mock objects, and the mock library is Python’s way (in Python 3.3+ the mock library became part of the standard library).
The mock library is all about a single core class - MagicMock
- that has
several unusual properties. Instead of writing a lot of different mock classes
like MockBar
above, you can just use the one MagicMock
class to easily
replace objects of any class that your tests need, without having to write a
lot of mock code.
Note: The mock library actually has two very similar classes -
Mock
and
MagicMock
.
The difference is that MagicMock
supports
Python’s magic methods
whereas Mock
doesn’t. This usually isn’t important, but
as the mock user guide says
it’s sensible to use MagicMock
by default. The Hypothesis tests tend to use
Mock
more often, though.
Note: Some people make distinctions between different types of replacement
objects used by tests, for example
Sinon.js has separate classes for fakes, spies, stubs, and mocks.
Python’s Mock
and MagicMock
are capable of playing all of these roles and
in this tutorial we’re just going to use the one word mock for everything.
You’ll find Mock
and MagicMock
used a lot in the Hypothesis tests - to
create mock objects to pass in to methods under test as arguments, and in many
other ways too. This post will demonstrate and explain the features of
Mock
/ MagicMock
so that you can understand what the tests are doing, and
use mocks yourself. As usual we won’t cover everything but will concentrate
on the features commonly used in the Hypothesis tests. You can go to
the mock website for the rest.
You can access any attribute on a MagicMock
Normally in Python you can only access a given attribute name on an object if
that object has an attribute with that name, otherwise you’ll get an
AttributeError
. For example here the foo
object has no attribute named bar
so trying to do foo.bar
raises AttributeError
:
>>> class Foo(object):
... pass
>>> foo = Foo()
>>> foo.bar
Traceback (most recent call last):
...
AttributeError: 'Foo' object has no attribute 'bar'
MagicMock
objects are different, you can access any attribute name on a mock
object and it will always return another mock object:
>>> import mock
>>> my_mock = mock.MagicMock()
>>> my_mock.foo
<MagicMock name='mock.foo' id='139654612385424'>
>>> my_mock.bar
<MagicMock name='mock.bar' id='139654612362320'>
>>> my_mock.whatever_name_you_want
<MagicMock name='mock.whatever_name_you_want' id='139654612287632'>
By default a MagicMock
never raises AttributeError
. This property of mock
objects allows tests to pass them in to the code under test instead of the
real objects that would be used in production, and have the code under test
still work. For example here’s a simple test for one behavior of an event queue:
def test_push_appends_event_to_queue(self):
event_queue = EventQueue()
event = mock.MagicMock()
event_queue.push(event)
assert list(event_queue.queue) == [event]
We push()
a mock object onto event_queue
instead of the real event object
that it’s expecting. Even if the EventQueue
code accesses some attributes of
the mock object it won’t crash since you can read any attribute of
a mock. And then we’re still able to test what we wanted to test - that it
appends the mock object we gave it to the queue:
assert list(event_queue.queue) == [event]
.
Each attribute returns a different other MagicMock
object
In the example above you can tell from the different MagicMock
id
s that each
attribute that is accessed - foo
, bar
or whatever_name_you_want
-
returns a different mock object:
>>> my_mock = mock.MagicMock()
>>> my_mock.foo
<MagicMock name='mock.foo' id='139654612385424'>
>>> my_mock.bar
<MagicMock name='mock.bar' id='139654612362320'>
>>> my_mock.whatever_name_you_want
<MagicMock name='mock.whatever_name_you_want' id='139654612287632'>
Two MagicMock
objects with different id
s are considered unequal:
>>> my_mock.foo == my_mock.bar
False
The same attribute always returns the same other MagicMock
Each different attribute of a MagicMock
object returns a different other
MagicMock
object. On the other hand, if you access the same attribute of a
MagicMock
multiple times it will always return the same other MagicMock
object with the same id
:
>>> my_mock.foo
<MagicMock name='mock.foo' id='139654612385424'>
>>> my_mock.foo
<MagicMock name='mock.foo' id='139654612385424'>
Two MagicMock
objects with the same id
are considered equal:
>>> my_mock.foo == my_mock.foo
True
Note: The same attribute name on two different MagicMock
s will return two different
other MagicMock
s though:
>>> my_mock = mock.MagicMock()
>>> my_other_mock = mock.MagicMock()
>>> my_mock.foo == my_other_mock.foo
False
Tests can make use of this property of mock objects because a given attribute of a mock will return the same other mock both when accessed by the code under test and when accessed by the test code itself, but if the code and the test access different attributes they’ll get different other mock objects.
For example lets look at a test for a simple presenter class.
AnnotationSearchIndexPresenter
is a class that takes an annotation object and provides an asdict()
method
that returns a dictionary representation of that annotation suitable for
storing in our Elasticsearch index. A simplified version of the class might
look something like this:
class AnnotationSearchIndexPresenter(object):
def __init__(self, annotation):
self.annotation = annotation
def asdict(self):
"""Return a search-indexable dictionary representation of self.annotation."""
return {
'target': [
{
'scope': self.annotation.target_uri_normalized,
...
},
...
],
...
}
AnnotationSearchIndexPresenter
translates and transforms attributes of the
annotation object into a dictionary with a different format and structure - the
format and structure required by Hypothesis’s search index. For example
annotation.target_uri_normalized
becomes the first item in a
['target'][0]['scope']
list in the dict that asdict()
returns (don’t ask why).
A simple test for this
could work by passing a mock object to AnnotationSearchIndexPresenter
:
def test_it_copies_target_uri_normalized_to_target_scope(self):
annotation = mock.MagicMock()
annotation_dict = AnnotationSearchIndexPresenter(annotation).asdict()
assert annotation_dict['target'][0]['scope'] == [annotation.target_uri_normalized]
The test passes because the first time our mock annotation’s
annotation.target_uri_normalized
attribute is accessed (by the
AnnotationSearchIndexPresenter.asdict()
code when the test calls it) it
returns the same other mock object as the second time it’s accessed (by the
assert
statement in the test itself). The assert
statement relies on this
property of mocks to pass. If AnnotationSearchIndexPresenter
had used
some other attribute of the mock annotation, say
annotation.target_uri
instead of annotation.target_uri_normalized
, that
would have returned a different other mock object and the assert
would have failed.
You can also set any attribute on a MagicMock
If you want an attribute of a MagicMock
to have some value other than
being another MagicMock
you can just set it as you could do with a normal
Python object:
>>> my_mock.foo = 23
>>> my_mock.foo
23
Tests can use this when they need the value of a mock object to be some other
type of object than a mock. For example Annotation.created
is a
datetime
object
and AnnotationSearchIndexPresenter
turns it into a string (maybe by calling
strftime
).
To test that AnnotationSearchIndexPresenter
formats this string correctly we
really want our mock annotation.created
to be a datetime
not another mock:
import datetime
def test_it_formats_created_into_a_string():
annotation = mock.MagicMock()
annotation.created = datetime.datetime(2016, 2, 24, 18, 3, 25, 768)
annotation_dict = AnnotationSearchIndexPresenter(annotation).asdict()
assert annotation_dict['created'] == '2016-02-24T18:03:25.000768+00:00'
Of course it’s also possible for the code under test to set an attribute of a mock object to a value, and then a test might assert that the mock’s attribute was set to the expected value:
def set_foo_to_bar(thing):
thing.foo = 'bar'
def test_it_sets_foo_to_bar():
thing = mock.MagicMock()
set_foo_to_bar(thing)
assert thing.foo == 'bar'
Code setting attributes of its dependencies is often not a great design, but this does come up sometimes.
You can pass any keyword args to the MagicMock
constructor
Unlike a typical Python object, you can pass any keyword argument to the
MagicMock
constructor and its value will be used as the value of the attribute
on the MagicMock
instead of returning another MagicMock
:
>>> my_mock = mock.MagicMock(foo=23, bar=True)
>>> my_mock.foo
23
>>> my_mock.bar
True
>>> my_mock.something_else
<MagicMock name='mock.something_else' id='139654612225680'>
For example instead of creating the mock and then setting the attribute on it,
the test above could have created the mock with created
already set:
annotation = mock.MagicMock(created=datetime.datetime(2016, 2, 24, 18, 3, 25, 768))
Of course multipe attributes can be set at once as well:
annotation = mock.MagicMock(
created=datetime.datetime(2016, 2, 24, 18, 3, 25, 768),
target_uri_normalized='http://example.com/normalized',
foo=True,
bar=23,
...
)
MagicMock
s are callable
You can also call a MagicMock
object as if it were a method or function,
and by default it’ll return another MagicMock
object just like accessing an
attribute does:
>>> my_mock = mock.MagicMock()
>>> my_mock()
<MagicMock name='mock()' id='140404522909008'>
This means that your tests can use MagicMock
s not only to replace objects
that the code under test uses, but also to replace functions and
methods. When the code under test tries to call a MagicMock
instead of
the real function or method it’ll work, and it will just get another
MagicMock
back as the return value.
As with accessing attributes, by default the same MagicMock
always returns
the same other MagicMock
when called but two different MagicMock
s return
two different other MagicMock
s:
>>> my_mock = mock.MagicMock()
>>> my_mock() == my_mock()
True
>>> my_other_mock = mock.MagicMock()
>>> my_mock() == my_other_mock()
False
You can pass any arguments or keyword arguments when calling a mock. It always return the same other mock, regardless of what arguments it was called with:
>>> my_mock(True)
<MagicMock name='mock()' id='140404522909008'>
>>> my_mock(1, 2, 3)
<MagicMock name='mock()' id='140404522909008'>
>>> my_mock("foo", bar=[27, False])
<MagicMock name='mock()' id='140404522909008'>
Note: A mock returns a different mock object when called, it doesn’t return itself:
>>> my_mock() == my_mock
False
The other mock that it returns is accessible as the special return_value
attribute:
>>> my_mock() == my_mock.return_value
True
One simple use of the callability of MagicMock
is to test that a function
returns the return value of another function that it depends on. In this code
the bar()
function is passed in to the foo()
function as an argument,
foo()
does a bunch of important stuff (perhaps to calculate the arguments
it’ll pass to bar()
) and then returns the result of calling bar()
:
def foo(bar):
...
return bar(arg1, arg2, ...)
Here’s a simple test for that last part of foo()
‘s behavior, the return
:
def test_foo_returns_what_bar_returned():
bar = mock.MagicMock()
returned = foo(bar)
assert returned == bar.return_value
Tip: We could have written assert returned == bar()
and it would have
worked the same. But as we’ll see below, MagicMock
s keep a record of each
time they’re called, so it’s a good habit for the test code itself not to call
its own MagicMock
s because this can pollute the call record. Even at times
when you could get away with it, it’s just good to be consistent and always use
the special return_value
attribute instead.
Tip: If you ever want a mock object that you don’t want the code under
test to be able to call there’s a separate class for that -
NonCallableMagicMock
.
You can ask a MagicMock
what calls have been made to it
For the foo()
method above we might want to test that it calls bar()
with
the right arguments, as well as that it returns the result of calling bar()
.
We can do this using MagicMock
‘s assert_called_once_with()
method:
def test_foo_calls_bar_correctly():
bar = mock.MagicMock()
foo(bar)
bar.assert_called_once_with("expected_arg_1", "expected_arg_2")
assert_called_once_with()
will raise an AssertionError
, causing the test
to fail, if foo()
does not call bar()
, if it calls bar()
more than
once, or if it calls bar()
with the wrong arguments.
These kind of assertions about how a MagicMock
was used are often useful in
testing integration functions - functions whose job is to glue together other
functions and methods, calling them with the right arguments and doing the right
things with their return values.
The mock library provides a whole collection of tools for testing how a mock was used, including:
-
called
- a simple attribute that’sTrue
if this mock has been called (any number of times, with any arguments) andFalse
otherwise:>>> my_mock = mock.MagicMock() >>> my_mock.called False >>> my_mock() <MagicMock name='mock()' id='139781392300624'> >>> my_mock.called True
-
call_count
- a simple attribute that counts the number of times this mock has been called:>>> my_mock = mock.MagicMock() >>> my_mock.call_count 0 >>> my_mock() <MagicMock name='mock()' id='139655375609360'> >>> my_mock.call_count 1 >>> my_mock(1, 'foo') <MagicMock name='mock()' id='139655375609360'> >>> my_mock.call_count 2
-
assert_called_with()
- fails unless the most recent call to the mock matches the given arguments. -
assert_called_once_with()
- fails unless the mock has been called exactly once, and with the given arguments. -
assert_any_call()
- fails unless the mock has been called at least once with the given argument (regardless of whether or not there have been other calls as well). -
And more. If you want to make more complex assertions about multiple calls to a mock and their arguments, and even what order the calls happened in, there are methods like
assert_has_calls()
, attributes likecall_args
andcall_args_list
, and helpers likecall()
andANY
. There’s even areset_mock()
method to reset a mock’s record of its calls mid test. See the documentation for theMock
class and the mock library’s helpers. You can also search the Hypothesis code for examples of these in use.
You can call any method on a MagicMock
Normally you can only call a method on an object if that object’s class has
that method, otherwise you’ll get AttributeError
:
>>> class Foo(object):
... def do_something(self):
... pass
...
...
>>> foo = Foo()
>>> foo.bar()
Traceback (most recent call last):
...
AttributeError: 'Foo' object has no attribute 'bar'
If you try to call a method that does exist, but you pass it a positional
or keyword argument that the method doesn’t have, you’ll get a TypeError
:
>>> foo.do_something(1)
Traceback (most recent call last):
...
TypeError: do_something() takes exactly 1 argument (2 given)
>>> foo.do_something(some_arg=12)
Traceback (most recent call last):
...
TypeError: do_something() got an unexpected keyword argument 'some_arg'
Since any attribute name on a MagicMock
is another MagicMock
,
and since MagicMock
s are callable, that means that as well as
accessing any attribute name on a MagicMock
you can also call any method on a MagicMock
and pass any positional or
keyword arguments:
>>> my_mock = mock.MagicMock()
>>> my_mock.foo()
<MagicMock name='mock.foo()' id='140404522292688'>
>>> my_mock.bar(16)
<MagicMock name='mock.bar()' id='140404522339536'>
>>> my_mock.whatever_method_name_you_want("foobar")
<MagicMock name='mock.whatever_method_name_you_want()' id='140404522411024'>
This means that if the code under test uses an object that it calls methods on,
a test can pass in a MagicMock
in place of that object and the code will still work:
def foo(bar):
...
return bar.some_method(arg1, arg2, ...)
def test_foo_returns_what_some_method_returned():
bar = mock.MagicMock()
returned = foo(bar)
assert returned == bar.some_method.return_value
Of course, each method returns another mock when called. The same method always returns the same other mock object (no matter what arguments its called with), but each different method returns a different other mock.
If foo()
had called bar.some_other_method()
instead, it would have
returned a different MagicMock
(some_other_method.return_value
rather than
some_method.return_value
) and the assert
would have failed.
Note: When you access a name on a mock object it returns a different other mock object to if you call that name:
>>> my_mock.foo() == my_mock.foo
False
The mock that calling returns is accessible as return_value
:
>>> my_mock.foo() == my_mock.foo.return_value
True
And of course, if you’ve set a certain attribute name on a mock to a non-callable value then you can’t call it anymore:
>>> my_mock.foo = 23
>>> my_mock.foo
23
>>> my_mock.foo()
Traceback (most recent call last):
...
TypeError: 'int' object is not callable
You can tell a MagicMock
method what value to return
We’ve seen that the special return_value
attribute
is the value that the mock will return if called (by default, another mock).
You can set return_value
to something else and the mock will return that when
called instead:
>>> my_mock()
<MagicMock name='mock()' id='140404522909008'>
>>> my_mock.return_value = 'custom return value'
>>> my_mock()
'custom return value'
This of course means that you can control the return value of any methods on the mock as well:
>>> my_mock.bar.return_value = 26.2
>>> my_mock.bar()
26.2
You can also set the return value of a mock as a constructor argument:
>>> my_mock = mock.MagicMock(return_value='custom_value')
>>> my_mock()
'custom_value'
>>> my_mock = mock.MagicMock(foo=mock.MagicMock(return_value='custom_value'))
>>> my_mock.foo()
'custom_value'
Tests can set the return_value
of a mock when they need it be something other
than another mock object. In one of our examples above we needed
annotation.created
to be a datetime
object rather than another MagicMock
.
If annotation.created()
were a method instead of an attribute then we might
have needed it to return a datetime
instead:
def test_it_formats_created_into_a_string():
annotation = mock.MagicMock()
annotation.created.return_value = datetime.datetime(2016, 2, 24, 18, 3, 25, 768)
annotation_dict = AnnotationSearchIndexPresenter(annotation).asdict()
assert annotation_dict['created'] == '2016-02-24T18:03:25.000768+00:00'
When AnnotationSearchIndexPresenter
calls annotation.created()
it will
get our datetime
object as the return value, and our assert
depends on that.
You can make a MagicMock
method raise an exception
side_effect
is another special attribute of mock objects.
If you set side_effect
to an exception class or exception object then the
mock will raise the exception when called:
>>> my_mock.side_effect = RuntimeError('Something broke')
>>> my_mock()
Traceback (most recent call last):
...
RuntimeError: Something broke
You can use this to test that a method under test handles exceptions properly when they’re raised by other functions or methods that the method under test calls.
As with return_value
you can also pass side_effect
as an argument to the
MagicMock()
constructor: my_mock = mock.MagicMock(side_effect=...)
.
A simple example of a mock raising an exception is a test for the EventQueue
class that we saw earlier. When EventQueue.publish_all()
is called it calls
the notify()
method of each event object on the queue. If one of those
notify()
methods raises an exception EventQueue
should not crash, but
should catch and log the exception:
def test_publish_all_logs_exception(self):
event = mock.MagicMock()
log = mock.MagicMock()
queue = eventqueue.EventQueue(log)
queue.push(event)
# Make event.notify() raise ValueError when called.
event.notify.side_effect = ValueError('exploded!')
# When publish_all() calls event.notify() it'll raise the ValueError,
# rather than crashing publish.all() should catch this exception and log it.
queue.publish_all()
assert log.exception.called
You can make a MagicMock
method return a different value each time
Sometimes your test needs a mock to return a sequence of different values each
time it’s called.
If you set side_effect
to an iterable then each time the mock is called it’ll
return the next item from that iterable:
>>> my_mock.side_effect = [1, 2, 3]
>>> my_mock()
1
>>> my_mock()
2
>>> my_mock()
3
>>> my_mock()
Traceback (most recent call last):
...
StopIteration
If one of the values that the iterable returns is an exception then the mock will raise an exception that time it’s called:
>>> my_mock.side_effect = [1, RuntimeError, 3]
>>> my_mock()
1
>>> my_mock()
Traceback (most recent call last):
...
RuntimeError
>>> my_mock()
3
You can attach a normal function to a MagicMock
method
Lastly, if you set side_effect
to a function then that function will be
called when the mock is called, and the mock will return whatever that
function returns. This can be useful when your test needs your mock to return
a value dynamically, for example to return
a value that depends on the value it was called with:
>>> def fake_encrypt(input):
... return input + "_encrypted"
...
>>> my_mock = mock.MagicMock()
>>> my_mock.encrypt.side_effect = fake_encrypt
>>> my_mock.encrypt("hello")
'hello_encrypted'
Setting side_effect
to a function also means that the mock can only be called
with the same arguments and keyword arguments that the side effect function
takes, otherwise you’ll get a TypeError
just as you would when calling any
normal Python function with the wrong arguments. This is one way that your
tests can ensure that the method under test never calls the mock with invalid arguments:
>>> my_mock.encrypt("arg1", "arg2")
Traceback (most recent call last):
...
TypeError: fake_encrypt() takes exactly 1 argument (2 given)
Conclusion
This concludes our tour of the MagicMock
class and its features.
The class has a lot of features, and this has been a long post,
but this should give you an understanding of most of what’s going on when mocks
are in use in the Hypothesis tests.
For the rest of the details, go to the mock website.
Know that you know how to use mocks, the next post will explain some of the dangers of using mocks.