Sometimes, you need "other" code resources for your test setup. But those resources may be unavailable, unstable, or just too unwieldy to use. You could try and find a replacement for the missing resource; or you could simulate it by creating what is known as a mock. Mocks let us simulate resources that are either unavailable or too unwieldy for unit testing.
Creating a mock in Python is done through the Mock module. You can build a mock one-attribute-at-a-time or wholesale with a dictionary object or a class interface. You can also define how a mock behaves and check its usage during a test. Let's explore.
Sample code presented here is written in Python 2.4, unless noted otherwise.
Reasons to Mock
There are times when a test resource is either unavailable or unsuitable. Perhaps the resource is being developed in parallel with the test subject. It may then be incomplete or too unstable to be reliable.
The resource may be too costly. If the resource is a third-party product, its high price tag can disqualify its use for testing. Setting up the resource might be complex, taking up hardware and time that could be used elsewhere. If the resource is a data source, setting up its data set, one that mimics real-world content, can get tedious.
The resource may simply be unpredictable. A good unit test has to be repeatable, allowing you to isolate and identify a fault. But the test resource might give out stochastic results, or it might have widely varying response times. And as a result, you end up glossing over a potential showstopper.
These are some of the reasons why you might want to use a mock in place of a test resource. A mock can present the same set of method interfaces as the resource to the test subject. But a mock is easier to setup, easier to manage. It can present the test subject the same method interfaces as the actual test resource. It can deliver deterministic results, and it can be customize to suit a particular test. And it can be easily updated to reflect changes in the actual resource.
Of course, mocks are not without issues. Designing an accurate mock is difficult, especially if you have no reliable information on the test resource. You can try and find an open-source match, or you can make a guesstimate on the resource's method interface. Whichever you choose, you can update the mock easily later on, should you get more detailed information on the preferred resource.
Too many mocks can complicate a test, making it harder for you to track down a failure. Best practice is to limit a test case to one or two mocks, or use separate test cases for each mock/subject pairing.
Mocks versus Stubs versus Fakes
A mock is not the only way to mimic a test resource. Solutions like a stub or a fake are just as capable of the same service. So, how does a mock compare against these two? Why choose a mock over a stub or a fake?
Consider the stub: A stub presents a set of method interfaces to the test subject, the same set the subject would see in an actual test resource. When the test subject calls a stub method, the stub may respond with a predetermined set of results. It may raise an error or exception, also predetermined. A stub may track its interactions with the test subject, but it performs no tasks outside the narrow scope of its programming.
A fake also presents a set of method interfaces and tracks its interactions with the test subject. But unlike a stub, a fake actually processes input data from the test subject and produces results based on that data. In short, a fake is a functional, but non-production version of the actual test resource. It lacks the checks and balances found in resource, it uses simpler algorithms, and it seldom, if ever, stores or transports data.
With fakes and stubs, you can test if the test subject called the right method with the right input. You can test how the subject handles the result and how it reacts to an error or exception. These tests are known as state verification. But what if you want to know if the test subject called the same method twice? What if you want to know if it called several methods in the proper order? Such tests are known as behavior verification, and to do them, you need mocks.
To Mock with Python
The Mock module is what you use to create and manage a mock object on Python. This module is the brainchild of Michael Foord, and it is a standard module on Python 3.0. For Python 2.4 to 2.7, however, you have to install the module yourself. You can get the latest version of the Mock module (1.0.1) from the Python Package Index website.
The typical test setup has at least two parts. First, there is the test subject (red), the focus of the test. It can be a method, module, or class. It may or may not return a result, but it can raise an error or exception depending on input data or internal state (Figure 1).
Second is the test case (grey), which runs alone or as part of a suite. It prepares the test subject, as well as any data or resources needed by the subject. It runs one or more test routines and checks how the test subject behaves in each test. And it collects the test results and presents them in a concise, readable format.
Now, some test subjects need one or more resources (green) in order to function. These resources may be another class or module, or even an independent process. Whatever their nature, test resources are functional code. Their role is to support the test subject, but they are not the focus of the test.
The Mock module provides a handful classes on which to base your mock object. There is even a patch mechanism for altering the mock on the fly. But for now, our focus is on one class: the
Figure 2 shows the basic structure of the
Mock class (green). It derives from two parent classes:
NonCallableMock defines the routines needed by the mock object. It overrides several magic methods, giving them default behaviors. And it supplies the assert routines for tracking the mock's behavior. As for
CallableMixin, it updates those magic methods that make a mock object callable. In turn, both parents derive from the
Base class (red), which declares the properties needed by the mock object.
Preparing to Mock
Mock class has four sets of methods (Figure 3). In the first set is the class constructor, which takes up to six optional and labeled arguments. The diagram shows the four arguments you will use most often.
The first constructor argument is the
name argument, which gives a mock object a unique name. Listing One shows how I create a mock object (
mockFoo) with the name
Foo. Notice the name appears next to the mock's unique ID when I print the mock object (lines 6-9).
from mock import Mock # create the mock object mockFoo = Mock(name = "Foo") print mockFoo # returns: <Mock name='Foo' id='494864'> print repr(mockFoo) # still returns: <Mock name='Foo' id='494864'>
The next constructor argument is the
spec argument. It sets the mock object's attributes which may be a property or a method. The attributes can be a list of strings or can come from another Python class.
To demonstrate, in Listing Two, I have a list object (
fooSpec) with three items (line 4): the property attribute
_fooValue, and the method attributes
doFoo. When I pass
fooSpec to the class constructor (line 7),
mockFoo gains three attributes, which I can access with the dot operator (lines 10-15). But if I access an undeclared attribute,
mockFoo raises an
AttributeError and names the "faulty" attribute (lines 21-24).
from mock import Mock # prepare the spec list fooSpec = ["_fooValue", "callFoo", "doFoo"] # create the mock object mockFoo = Mock(spec = fooSpec) # accessing the mocked attributes print mockFoo # <Mock id='427280'> print mockFoo._fooValue # returns <Mock name='mock._fooValue' id='2788112'> print mockFoo.callFoo() # returns: <Mock name='mock.callFoo()' id='2815376'> mockFoo.callFoo() # nothing happens, which is fine # accessing the missing attributes print mockFoo._fooBar # raises: AttributeError: Mock object has no attribute '_fooBar' mockFoo.callFoobar() # raises: AttributeError: Mock object has no attribute 'callFoobar'
Listing Three shows another use of the
spec argument. This time, I have class
Foo with the same three attributes (lines 4-12). I pass the class name to the constructor (line 15), which then produces a mock object with the same attributes as
Foo (lines 18-23). Again, accessing an undeclared attribute raises an
AttributeError (lines 29-32). Also, in both examples, the method attributes are non-functional. Calling a mocked method does nothing, even if said method has functional code (lines 25-26).
from mock import Mock # The class interfaces class Foo(object): # instance properties _fooValue = 123 def callFoo(self): print "Foo:callFoo_" def doFoo(self, argValue): print "Foo:doFoo:input = ", argValue # create the mock object mockFoo = Mock(spec = Foo) # accessing the mocked attributes print mockFoo # returns <Mock spec='Foo' id='507120'> print mockFoo._fooValue # returns <Mock name='mock._fooValue' id='2788112'> print mockFoo.callFoo() # returns: <Mock name='mock.callFoo()' id='2815376'> mockFoo.callFoo() # nothing happens, which is fine # accessing the missing attributes print mockFoo._fooBar # raises: AttributeError: Mock object has no attribute '_fooBar' mockFoo.callFoobar() # raises: AttributeError: Mock object has no attribute 'callFoobar'
The next constructor argument is
return_value. This one sets a mock object's response when it gets a direct call. I use this argument for simulating a factory call.
To demonstrate, in Listing Four, I have
return_value set to
456 (line 4). When I call
mockFoo, I get
456 for a result (line 9-11). In Listing Five, I pass an instance of class
return_value (lines 15-19). Now, when I call
mockFoo, I get
fooObj instead (shown here as
mockObj) (line 24). And unlike in Listings Two and Three, the methods in
mockObj are functional.
from mock import Mock # create the mock object mockFoo = Mock(return_value = 456) print mockFoo # <Mock id='2787568'> mockObj = mockFoo() print mockObj # returns: 456
from mock import Mock # The mock object class Foo(object): # instance properties _fooValue = 123 def callFoo(self): print "Foo:callFoo_" def doFoo(self, argValue): print "Foo:doFoo:input = ", argValue # creating the mock object fooObj = Foo() print fooObj # returns: <__main__.Foo object at 0x68550> mockFoo = Mock(return_value = fooObj) print mockFoo # returns: <Mock id='2788144'> # creating an "instance" mockObj = mockFoo() print mockObj # returns: <__main__.Foo object at 0x68550> # working with the mocked instance print mockObj._fooValue # returns: 123 mockObj.callFoo() # returns: Foo:callFoo_ mockObj.doFoo("narf") # returns: Foo:doFoo:input = narf <Mock id='428560'>
return_value is the
side_effect argument. This one assigns the mock with an alternative result, one that overrides
return_value. In short, a simulated factory call returns the
side_effect value, not the
Listing Six demonstrates the effects of the
side_effect argument. First, I create an instance of class
fooObj) and pass it as a
return_value argument (line 17). The result is similar to Listing Five;
fooObj when called (line 22). But then I repeat the same steps, passing
StandardError for the
side_effect argument (line 28). Now, calling
StandardError, instead of returning
fooObj (lines 29-30).
from mock import Mock # The mock object class Foo(object): # instance properties _fooValue = 123 def callFoo(self): print "Foo:callFoo_" def doFoo(self, argValue): print "Foo:doFoo:input = ", argValue # creating the mock object (without a side effect) fooObj = Foo() mockFoo = Mock(return_value = fooObj) print mockFoo # returns: <Mock id='2788144'> # creating an "instance" mockObj = mockFoo() print mockObj # returns: <__main__.Foo object at 0x2a88f0> # creating a mock object (with a side effect) mockFoo = Mock(return_value = fooObj, side_effect = StandardError) mockObj = mockFoo() # raises: StandardError
Listing Seven shows another effect. In this one, I pass a list object (
fooList) to the class constructor (lines 17-18). Then, each time I call
mockFoo, it returns a list item in succession (lines 20-30). Once
mockFoo reaches the end of the list, another call will raise a
StopIteration error (lines 32-34).
from mock import Mock # The mock object class Foo(object): # instance properties _fooValue = 123 def callFoo(self): print "Foo:callFoo_" def doFoo(self, argValue): print "Foo:doFoo:input = ", argValue # creating the mock object (with a side effect) fooObj = FooSpec() fooList = [665, 666, 667] mockFoo = Mock(return_value = fooObj, side_effect = fooList) fooTest = mockFoo() print fooTest # returns 665 fooTest = mockFoo() print fooTest # returns 666 fooTest = mockFoo() print fooTest # returns 667 fooTest = mockFoo() print fooTest # raises: StopIteration
You could pass other iterable objects (
set, tuple) to the
side_effect argument. What you cannot pass is a primitive (integer, string, and so on) because these are not iterable. To make a primitive iterable, add it to a single-item list.
Asserting with a Mock
The next set of methods from the
Mock class are the asserts. These help track the method calls made to the mock by the test subject. They can work in conjunction with the asserts from the unittest module. They can be attached to the mock or to one of its method attributes. All but one take the same two optional arguments: a variable sequence, and a key/value sequence.
The first assert,
assert_called_with(), checks if a mocked method gets the right arguments. It fires when at least one argument has the wrong value or type, when there is a wrong number of arguments, when the arguments are in the wrong order, or when the mocked method is not expecting any arguments at all. Listing Eight shows how you might use this assert. Here, I prepared a mock object with class
Foo as its spec. I call the mocked method
doFoo(), passing a string for input. With
assert_called_with(), I check if the method gets the right input. The assert in line 20 passes because
"narf" for input. But the assert in line 24 fails because
"zort", which is wrong.
from mock import Mock # The mock object class Foo(object): # instance properties _fooValue = 123 def callFoo(self): pass def doFoo(self, argValue): pass # create the mock object mockFoo = Mock(spec = Foo) print mockFoo # returns <Mock spec='Foo' id='507120'> mockFoo.doFoo("narf") mockFoo.doFoo.assert_called_with("narf") # assertion passes mockFoo.doFoo("zort") mockFoo.doFoo.assert_called_with("narf") # AssertionError: Expected call: doFoo('narf') # Actual call: doFoo('zort')