Channels ▼
RSS

Design

Patching Mocks in Python


In Listing Seven, I added argFoo to the argument list and used it in my routine code (line 14). But note mockFoo is the last argument in the list. If I reverse the order as follows:

@patch('__main__.Foo')
def testFoo(mockFoo, argFoo)
  #...see Listing Seven... 

testFoo() raises a TypeError when it tries to treat argFoo as a mock object.

Patching with the Core Decorator

Let's look at several patching examples, beginning with the core decorator. The target resource is still class Foo. The examples use a patcher object to apply the patch. And they all have the import statement:

from mock import patch, MagicMock

Listing Eight is similar to Listing Four, except I passed a True to the spec argument (line 2). The result is a MagicMock instance (mockFoo) with the same attributes as class Foo (lines 6-8). If I attempt a constructor call (line 14), I get a non-callable copy of mockFoo, still with the same Foo attributes (lines 16-18).

Listing Eight

# start patching
fooPatch = patch('__main__.Foo', spec = True)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doFoo()
print mockFoo.callFoo("narf")
# == results == 
# <MagicMock name='Foo' spec='Foo' id='429520'>
# <MagicMock name='Foo.doFoo()' id='3008240'>
# <MagicMock name='Foo.callFoo()' id='3093232'>

mockFoo2 = mockFoo()

print mockFoo2
print mockFoo2.doFoo()
print mockFoo2.callFoo("narf")
# == results == 
# <NonCallableMagicMock name='Foo()' spec='Foo' id='448080'>
# <MagicMock name='Foo().doFoo()' id='2604816'>
# <MagicMock name='Foo().callFoo()' id='2661136'>

# stop patching
fooPatch.stop()


# start patching
fooPatch = patch('__main__.Foo', spec = False)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doFoo()
print mockFoo.callFoo("narf")
# == results == 
# <MagicMock name='Foo' id='429584'>
# <MagicMock name='Foo.doFoo()' id='3012144'>
# <MagicMock name='Foo.callFoo()' id='3097136'>

mockFoo2 = mockFoo()

print mockFoo2
print mockFoo2.doFoo()
print mockFoo2.callFoo("narf")
# == results == 
# <MagicMock name='Foo()' id='2606256'>
# <MagicMock name='Foo().doFoo()' id='2662576'>
# <MagicMock name='Foo().callFoo()' id='2690192'>

# stop patching
fooPatch.stop()

What if I pass a False to the spec argument (line 29)? I still get a MagicMock instance with the same Foo attributes. But when I do a constructor call (line 41), I get a callable copy of mockFoo.

Listing Nine shows what happens when I pass a class for a spec. The class in question is Bar (lines 1-5). I pass Bar to the spec argument (line 8), and I get a MagicMock instance (mockFoo), whose attributes are from class Bar (lines 12-14). If I try to access a Foo attribute (line 20), I get an AttributeError for a result (line 20).

Listing Nine

class Bar(object):
	def doBar(self):
		return("Bar:doBar:")
	def callBar(self, anArg):
		print "Bar:callBar_:", anArg

# start patching
fooPatch = patch('__main__.Foo', spec = Bar)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doBar()
print mockFoo.callBar("narf")
# == results ==
# <MagicMock name='Foo' spec='Bar' id='429776'>
# <MagicMock name='Foo.doBar()' id='2985680'>
# <MagicMock name='Foo.callBar()' id='3062480'>

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

# stop patching
fooPatch.stop()


# creating a resource object
testBar = Bar()

# start patching
fooPatch = patch('__main__.Foo', spec = testBar)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doBar()
print mockFoo.callBar("narf")
# returns:
# <NonCallableMagicMock name='Foo' spec='Bar' id='448080'>
# <MagicMock name='Foo.doBar()' id='2985776'>
# <MagicMock name='Foo.callBar()' id='3062576'>

# stop patching
fooPatch.stop()

What if I pass a Bar instance for a spec (line 29, 32)? I still get a MagicMock instance with attributes taken from class Bar (lines 36-38). This time, however, the mock is a non-callable variant of MagicMock.

Finally, Listing Ten shows what happens when I pass a list to the spec argument. Here, I define a list object (fooSpec) with two entries (line 2). I pass fooSpec to the core decorator and got a non-callable MagicMock instance in return (lines 9-11). Furthermore, the attributes in mockFoo came only from fooSpec, not from class Foo. If I attempt to access a Foo attribute, again I get an AttributeError for a result.

Listing Ten

# creating a spec object
fooSpec = ["fooBar", "barFoo"]

# start patching
fooPatch = patch('__main__.Foo', spec = fooSpec)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.fooBar()
print mockFoo.barFoo
# returns:
# <NonCallableMagicMock name='Foo' id='429648'>
# <MagicMock name='Foo.fooBar()' id='3042896'>
# <MagicMock name='Foo.barFoo' id='3043088'>

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

# stop patching
fooPatch.stop()

Listing Eleven shows how the new argument affects the decorator. First, I create an instance of class Bar (testBar, line 2), then pass that instance to the decorator (line 8). The result is a mock object (mockFoo) that happens to be the testBar instance itself. When I access each Bar attribute, I get the encoded results defined by the class (12-14). And when I try to access a Foo attribute, I get an AttributeError in response (line 20).

Listing Eleven

# instantiate class Bar
testBar = Bar()
print testBar
# returns:
# <__main__.Bar object at 0x686d0>

# start patching
fooPatch = patch('__main__.Foo', new = testBar)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doBar()
print mockFoo.callBar("narf")
# == results ==
# <__main__.Bar object at 0x686d0>
# Bar:doBar:
# Bar:callBar_: narf

print mockFoo.doFoo()
# returns: None
# raises:
# AttributeError: 'Bar' object has no attribute 'doFoo'

# stop patching
fooPatch.stop()


# start patching
fooPatch = patch('__main__.Foo', new = testBar, spec = True)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doBar()
print mockFoo.callBar("narf")
# == results ==
# <__main__.Bar object at 0x686d0>
# Bar:doBar:
# Bar:callBar_: narf

print mockFoo.doFoo()
# returns: None
# raises:
# AttributeError: 'Bar' object has no attribute 'doFoo'

# stop patching
fooPatch.stop()

Suppose I also set the spec argument to True (line 30). I still get the same mockFoo instance, with only Bar attributes, none of Foo's (lines 34-36). This implies the core decorator "favors" new over spec.

Listing Twelve shows the impact of the autospec argument. If I set autospec to False (line 2), I get a MagicMock instance (mockFoo) with the same attributes as class Foo (lines 6-8). Moreover, a constructor call to mockFoo (line 14) gives me a copy of mockFoo, again with the same Foo attributes.

Listing Twelve

# start patching
fooPatch = patch('__main__.Foo', autospec = False)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doFoo()
print mockFoo.callFoo("narf")
# == returns ==
# <MagicMock name='Foo' id='429552'>
# <MagicMock name='Foo.doFoo()' id='3008016'>
# <MagicMock name='Foo.callFoo()' id='3093008'>

mockFoo2 = mockFoo()
print mockFoo2
# returns:
# <MagicMock name='Foo()' id='2602128'>

# stop patching
fooPatch.stop()


# start patching
fooPatch = patch('__main__.Foo', autospec = True)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doFoo()
print mockFoo.callFoo("narf")
# == returns ==
# <MagicMock name='Foo' spec='Foo' id='429552'>
# <MagicMock name='Foo.doFoo()' id='2980240'>
# <MagicMock name='Foo.callFoo()' id='3046448'>

mockFoo2 = mockFoo()
print mockFoo2
# returns:
# <NonCallableMagicMock name='Foo()' spec='Foo' id='448048'>

# stop patching
fooPatch.stop()


# start patching
fooPatch = patch('__main__.Foo', autospec = Bar)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doBar()
print mockFoo.callBar("narf")
# == returns ==
# <MagicMock name='Foo' spec='Bar' id='429776'>
# <MagicMock name='Foo.doBar()' id='2984592'>
# <MagicMock name='Foo.callBar()' id='3026224'>

mockFoo2 = mockFoo()
print mockFoo2
# returns:
# <NonCallableMagicMock name='Foo()' spec='Bar' id='448944'>

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

# stop patching
fooPatch.stop()

If I set autospec to True (line 24), I still get the same mockFoo with the same Foo attributes (lines 28-30). But this time, a constructor call (line 36) gives me a non-callable copy of mockFoo.

Finally, if I set autospec to class Bar (line 46), I get a MagicMock instance (mockFoo) whose attributes came from Bar (lines 50-52). A constructor call (line 58) gives me a non-callable copy of mockFoo, and a call to any Foo method attribute (line 63) raises an AttributeError.

Listing Thirteen shows the impact of the argument new_callable. First, I set new_callable to Bar (line 2). The result is not the usual MagicMock instance, but an actual instance of class Bar. When I interact with Bar's method attributes, the responses I get are those defined by the class (lines 6-8). What if I pass a Bar instance (testBar) to new_callable (line 19-22)? In this case, the core decorator raises a TypeError (line 23), informing me that testBar is not a callable object.

Listing Thirteen

# start patching
fooPatch = patch('__main__.Foo', new_callable = Bar)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doBar()
print mockFoo.callBar("narf")
# == returns ==
# <__main__.Bar object at 0x68eb0>
# Bar:doBar:
# Bar:callBar_: narf

# stop patching
fooPatch.stop()


# create the test object
testBar = Bar()

# start patching
fooPatch = patch('__main__.Foo', new_callable = testBar)
mockFoo = fooPatch.start()
# raises:
# TypeError: 'Bar' object is not callable

# stop patching
fooPatch.stop()

Listing Fourteen shows how to pass other attributes to the core decorator. Here, I pass the attributes makeFoo and giveFoo to the decorator function (line 2). I have makeFoo set to 123, giveFoo to "narf". The mock still has the same attributes as class Foo (lines 6-8). But now, I can access makeFoo and giveFoo as if they are legitimate properties of mockFoo (lines 14-15). However, if I treat makeFoo or giveFoo as methods (line 20), I get a TypeError in response.

Listing Fourteen

# start patching
fooPatch = patch('__main__.Foo', makeFoo = 123, giveFoo = 'narf')
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doFoo()
print mockFoo.callFoo("narf")
# == returns ==
# <MagicMock name='Foo' id='429584'>
# <MagicMock name='Foo.doFoo()' id='3016272'>
# <MagicMock name='Foo.callFoo()' id='3097168'>

print mockFoo.makeFoo
print mockFoo.giveFoo
# == returns ==
# 123
# narf

print mockFoo.makeFoo()
# raises:
# TypeError: 'int' object is not callable

# stop patching
fooPatch.stop()

Listing Fifteen shows another way to pass attributes. Now I have a sequence of key/value pairs (fooList, line 2). Each key is a method name and its attribute (return_value or side_effect), each value is the attribute's value. I pass fooList to the core decorator, taking care to add the ** prefix (line 5). I then get a mock (fooMock), whose instance methods return the values set by fooList (lines 10, 19). Also, if a method (such as makeFoo) is not defined by class Foo, the decorator will add it to mockFoo (line 15).

Listing Fifteen

# prepare the key/value argument
fooList = {'doFoo.return_value': "Foo:doFoo:narf", 'callFoo.side_effect': StandardError, 'makeFoo.return_value': "Foo:makeFoo:zort"}

# start patching
fooPatch = patch('__main__.Foo', **fooList)
mockFoo = fooPatch.start()

# the patched resource
print mockFoo
print mockFoo.doFoo()
# == results ==
# <MagicMock name='Foo' id='429520'>
# Foo:doFoo:narf

print mockFoo.makeFoo()
# returns:
# Foo:makeFoo:zort

print mockFoo.callFoo("narf")
# raises:
# StandardError

# stop patching
fooPatch.stop()

What if I use a Python object as the target? In Listing Sixteen, I created an instance of class Foo (testFoo), then passed testFoo to the core decorator (line 2, 8). When I call the start() method (line 9), the decorator raises an AttributeError, showing that it does not work on a object.

Listing Sixteen

# create an instance of the resource
testFoo = Foo()
print testFoo
# returns: 
# <__main__.Foo object at 0x811d0>

# start patching
fooPatch = patch(testFoo)
mockFoo = fooPatch.start()
# raises:
# AttributeError: 'Foo' object has no attribute 'rsplit'
fooPatch.stop()

Patching with Other Decorators

What about the other decorators? How do they go about patching a target resource?

Let's start with patch.object. Listing Seventeen shows how patch.object modifies a property. I pass class Foo and its attribute myFoo to patch.object. I also pass the string value I want myFoo to have (line 2). The decorator returns, then returns a reference to myFoo (fooTemp, line 3). When I print fooTemp to stdout (line 6), I get the string value set by patch.object. If I access myFoo directly from class Foo, I get the same value (line 7). And if I create an instance of Foo, then access the myFoo property, I still get the same string value (line 12-14). Outside the scope of patch.object, however, creating a Foo instance, then accessing myFoo will give the original value defined by class Foo (line 22-24).

Listing Seventeen

# start patching
fooPatch = patch.object(Foo, 'myFoo', "Foo:myFoo:narf")
fooTemp = fooPatch.start()

# the patched resource
print fooTemp
print Foo.myFoo
# == results ==
# Foo:myFoo:narf
# Foo:myFoo:narf

testFoo = Foo()
print testFoo
print testFoo.myFoo
# == results ==
# <__main__.Foo object at 0x68cd0>
# Foo:myFoo:narf

# stop patching
fooPatch.stop()

testFoo = Foo()
print Foo.myFoo
print testFoo.myFoo
# == results ==
# Foo:myFoo
# Foo:myFoo

Listing Eighteen shows how patch.object modifies a method. Here, I pass two arguments: the target Foo and the method name doFoo (line 8). In turn, patch.object returns a reference to doFoo, which goes into the variable fooTemp. I change doFoo's return_value attribute, then proceed to create an instance of Foo (lines 12-14). If I now invoke doFoo(), I get the value set earlier.

Listing Eighteen

# before patching
testFoo = Foo()
print testFoo.doFoo()
# returns:
# Foo:doFoo:

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

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

# stop patching
fooPatch.stop()


# start patching (variant 1)
fooPatch = patch.object(Foo, 'doFoo.return_value', "Foo:doFoo:narf")
fooTemp = fooPatch.start()
# raises
# AttributeError: <class '__main__.Foo'> does not have the attribute 'doFoo.return_value'
 
# stop patching
fooPatch.stop()


# start patching (variant 2)
fooPatch = patch.object(Foo, 'doFoo', "Foo:doFoo:narf")
fooTemp = fooPatch.start()

testFoo = Foo()
print testFoo.doFoo
# returns: 
# Foo:doFoo:narf
print testFoo.doFoo()
# raises:
# TypeError: 'str' object is not callable

# stop patching
fooPatch.stop()

If I pass both method name and return attribute, together with the desired value (line 23), this causes patch.object to raise an AttributeError, informing me that Foo does not have a method attribute named "doFoo.return_value". What if I pass just the method name and desired return value (line 33). This time, invoking doFoo as a property (line 37) returns the value provided, but invoking doFoo as a method (line 40) raises a TypeError.

Listing Nineteen shows how patch.object works with a spec argument. Here, I pass class Bar as the spec (line 2). Foo is still the target class, doFoo the target attribute. The decorator returns a MagicMock instance (fooTemp), with a name of doFoo and with attributes from Bar (lines 6-8). If I create an instance of Foo, then access the attribute doFoo, I get the MagicMock instance from fooTemp (lines 14-15). But if I invoke the instance method doFoo() (line 19), I get an object ID different from the one in fooTemp. It appears patch.object gave Foo two attributes, the property doFoo and the method doFoo().

Listing Nineteen

# start patching
fooPatch = patch.object(Foo, 'doFoo', spec = Bar)
fooTemp = fooPatch.start()

# the patched resource
print fooTemp
print fooTemp.doBar()
print fooTemp.callBar("narf")
# returns:
# <MagicMock name='doFoo' spec='Bar' id='429584'>
# <MagicMock name='doFoo.doBar()' id='2989712'>
# <MagicMock name='doFoo.callBar()' id='3062416'>

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

print testFoo.doFoo()
# returns:
# <MagicMock name='doFoo()' id='3099920'>
testFoo.doFoo.return_value = "Foo:doFoo:narf"
print testFoo.doFoo()
# returns:
# Foo:doFoo:narf

print testFoo.doFoo
# <MagicMock name='doFoo' spec='Bar' id='429584'>

# stop patching
fooPatch.stop()


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