Channels ▼
RSS

Design

Patching Mocks in Python


Listing Twenty shows how patch.object reacts to the autospec argument. Here again, I have Foo and doFoo as the first two arguments. Then I set the autospec argument to True. In turn, patch.object produces a function object (fooTemp) named doFoo. If I create a Foo instance (newFoo), then invoke doFoo() (lines 10-11), I get a MagicMock instance for a result. If I assign a value to the return_value attribute of fooTemp, invoking doFoo() gives me that return value (lines 15-16).

Listing Twenty

# start patching
fooPatch = patch.object(Foo, 'doFoo', autospec = True)
fooTemp = fooPatch.start()

# the patched resource
print fooTemp
# returns:
# <function doFoo at 0x64cf0>

testFoo = Foo()
print testFoo.doFoo()
# returns:
# <MagicMock name='doFoo()' id='506256'>

fooTemp.return_value = "Foo:doFoo:narf"
print testFoo.doFoo()
# returns:
# Foo:doFoo:narf

# stop patching
fooPatch.stop()

Now, we turn our attention to patch.dict. In Listing Twenty-one, I define a dictionary object (fooDict) with two key/value pairs (line 2). I pass fooDict to patch.dict, and I passed another key/value pair as the values argument (line 5). At first, patch.dict returns a None as the patched result (lines 6-7). But if I print fooDict to stdout (line 12), I find it contains three pairs, instead of two. So, patch.dict has added the pair from the value argument to fooDict. And if I print fooDict outside the scope of patch.dict (line 20), I find fooDict reverting to the original two pairs.

Listing Twenty-one

# before patching
fooDict = {'narf':123, 'zort':"abc"}

# start patching
fooPatch = patch.dict(fooDict, values = {'poink':616})
fooTemp = fooPatch.start()
print fooTemp
# returns:
# None

# the patched dictionary
print fooDict
# returns:
# {'zort': 'abc', 'narf': 123, 'poink': 616}

# stop patching
fooPatch.stop()

# after patching
print fooDict
# returns:
# {'zort': 'abc', 'narf': 123}

Listing Twenty-two is similar to Listing Twenty-one, except I pass a True for the clear argument (line 5). Here again, patch.dict returns a None as the "patched" result. When I print fooDict to stdout, I see only the key/value pair from the values argument (line 9). But outside the scope of patch.dict, fooDict gets back its original contents (line 17).

Listing Twenty-two

# before patching
fooDict = {'narf':123, 'zort':"abc"}

# start patching
fooPatch = patch.dict(fooDict, values = {'poink':616}, clear = True)
fooTemp = fooPatch.start()

# the patched resource
print fooDict
# returns:
# {'poink': 616}

# stop patching
fooPatch.stop()

# after patching
print fooDict
# returns:
# {'zort': 'abc', 'narf': 123}

In Listing Twenty-three I have a third sequence of key/value pairs (fooArgs, line 5). I pass this to patch.dict, taking care to prefix the variable with ** (line 6). The result is that fooDict now has five entries: its original contents, the key/value pair from the values argument, and the pairs from fooArgs (line 10). And if I access fooDict outside of patch.dict, I should find it back to its original contents.

Listing Twenty-three

# test resource
fooDict = {'narf':123, 'zort':"abc"}

# start patching
fooArgs = {'pinky':"egad", 'brain':"brilliant"}
fooPatch = patch.dict(fooDict, values = {'poink':616}, **fooArgs)
fooTemp = fooPatch.start()

# the patched resource
print fooDict
# returns:
# {'poink': 616, 'zort': 'abc', 'pinky': 'egad', 'narf': 123, 'brain': 'brilliant'}

# stop patching
fooPatch.stop()

Finally, let's look at patch.multiple. In Listing Twenty-four, I create two instances objFoo and objBar (lines 6-7). I pass these instances to patch.multiple and set them to the value of DEFAULT (line 15). In turn, the decorator returns a list of MagicMock instances (mockList), one for each instance. I can access each mock directly from mockList, or I can use the arguments objFoo and objBar instead (lines 22-23). Each mock has the same attributes as its corresponding instance (lines 28-29). Outside the scope of patch.multiple, objFoo and objBar revert to their original instances (lines 37-38).

Listing Twenty-four

from mock import patch, DEFAULT, MagicMock

#...

# prepare the test resources
objFoo = Foo()
objBar = Bar()
print objFoo
print objBar
# returns:
# <__main__.Foo object at 0x68710>
# <__main__.Bar object at 0x68b70>

# start patching
fooPatch = patch.multiple('__main__', objFoo = DEFAULT, objBar = DEFAULT)
mockList = fooPatch.start()
print mockList
# returns:
# {'objBar': <MagicMock name='objBar' id='429840'>, 'objFoo': <MagicMock name='objFoo' id='3043088'>}

# the patched resources
print objFoo
print objBar
# returns:
# <MagicMock name='objFoo' id='3043088'>
# <MagicMock name='objBar' id='429840'>

print objFoo.doFoo()
print objBar.doBar()
# returns:
# <MagicMock name='objFoo.doFoo()' id='3058192'>
# <MagicMock name='objBar.doBar()' id='2602480'>

# stop patching
fooPatch.stop()

print objFoo
print objBar
# returns:
# <__main__.Foo object at 0x68710>
# <__main__.Bar object at 0x68b70>

Note the use of the DEFAULT value. This is a helper object; it tells patch.multiple to use MagicMock for the patched resource. It must be imported by changing the import statement as follows:

from mock import patch, MagicMock, DEFAULT

Note the target __main__ used by patch.multiple. This tells the decorator that objFoo and objBar, and their respective classes, are in the same file as the decorator.

In Listing Twenty-five, I set objFoo to class Bar, but left objBar set to DEFAULT (line 8). The resulting list (mockList) now has one MagicMock instance, and it belongs to objBar. As for objFoo, it holds a reference to class Bar. I can use objFoo to make another Bar instance — a functional one, unlike the mock in objBar. And again, leaving the scope of patch.multiple, objFoo and objBar revert to their pre-patch forms (lines 28-29).

Listing Twenty-five

print objFoo
print objBar
# returns:
# <__main__.Foo object at 0x68710>
# <__main__.Bar object at 0x68b70>

# start patching
fooPatch = patch.multiple('__main__', objFoo = Bar, objBar = DEFAULT)
mockList = fooPatch.start()
print mockList
# returns:
# {'objBar': <MagicMock name='objBar' id='429840'>}

# the patched resource
print objFoo
print objBar
# returns:
# <class '__main__.Bar'>
# <MagicMock name='objBar' id='429840'>

print objFoo()
# returns:
# <__main__.Bar object at 0x73250>

# stop patching
fooPatch.stop()

print objFoo
print objBar
# returns:
# <__main__.Foo object at 0x68710>
# <__main__.Bar object at 0x68b70>

In Listing Twenty-six, I gave patch.multiple a spec argument. The spec is class Narf (lines 1-4), with a property myNarf and a method doNarf(). The resulting list (mockList) shows two MagicMock instances, both using Narf as spec (lines 8, 14-15). I can access myNarf and doNarf from either objFoo or objBar (lines 20-21, 26, 27). But if I try to access the original attributes, such as doFoo(), I get an AttributeError in response (line 32).

Listing Twenty-six

class Narf(object):
	myNarf = IOError
	def doNarf(self):
		return (123)

# start patching
fooPatch = patch.multiple('__main__', objFoo = DEFAULT, objBar = DEFAULT, spec = Narf)
mockList = fooPatch.start()
print mockList
# returns:
# {'objBar': <MagicMock name='objBar' spec='Narf' id='450640'>, 'objFoo': <MagicMock name='objFoo' spec='Narf' id='453648'>}

# the patched resources
print objFoo
print objBar
# returns:
# <MagicMock name='objFoo' spec='Narf' id='453648'>
# <MagicMock name='objBar' spec='Narf' id='450640'>

print objFoo.doNarf()
print objBar.doNarf()
# returns:
# <MagicMock name='objFoo.doNarf()' id='3017136'>
# <MagicMock name='objBar.doNarf()' id='3093904'>

print objFoo.myNarf
print objBar.myNarf
# returns:
# <MagicMock name='objFoo.myNarf' id='3094096'>
# <MagicMock name='objBar.myNarf' id='2624080'>

print objFoo.doFoo()
# raises:
# AttributeError: Mock object has no attribute 'doFoo'

# stop patching
fooPatch.stop()

Testing with Patches

How could we incorporate patching into a typical unit test setup? Consider the setup in Figure 3. The Order class is the test subject. It models a basic purchase order, consisting of an item name and quantity. The Warehouse class is the test resource. It serves as the data source, returning a True for an existing item and an integer for the requested quantity. It also serves as spec for any mock object. Finally, the OrderTest class is the test case, derived from the TestCase class.

patching mocks in python
Figure 3.

This setup is the same one I used in my previous article on Python mocks. It is also a simplified version of the setup featured in Martin Fowler's article Mocks Aren't Stubs.


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