Channels ▼
RSS

Testing

Using Mocks in Python


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).

Python
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 Mock class.

Figure 2 shows the basic structure of the Mock class (green). It derives from two parent classes: NonCallableMock and CallableMixin (grey). 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.

Python
Figure 2.

Preparing to Mock

The 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.

Python
Figure 3.

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).

Listing One

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 callFoo and 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).

Listing Two

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).

Listing Three

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 Foo (fooObj) for 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.

Listing Four

from mock import Mock

# create the mock object
mockFoo = Mock(return_value = 456)

print mockFoo
# <Mock id='2787568'>

mockObj = mockFoo()
print mockObj
# returns: 456

Listing Five

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'>

Counter to 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 return_value.

Listing Six demonstrates the effects of the side_effect argument. First, I create an instance of class Foo (fooObj) and pass it as a return_value argument (line 17). The result is similar to Listing Five; mockFoo returns fooObj when called (line 22). But then I repeat the same steps, passing StandardError for the side_effect argument (line 28). Now, calling mockFoo raises StandardError, instead of returning fooObj (lines 29-30).

Listing Six

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).

Listing Seven

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 doFoo() got "narf" for input. But the assert in line 24 fails because doFoo() got "zort", which is wrong.

Listing Eight

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')


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video