I frequently use the Mock library for unit testing in python. I needed a quick reference for my favorite functionality and couldn’t find one. I decided to make a lighthearted attempt at writing one while watching Kung Fu Panda on the telly. I hope others find it useful.
Before I start, here is the list of my favorites:
Mock classes
Mock class methods
Mock instances
Mock instance methods
Configurable return values
Restricted API for mock objects
MagicMock
Mock multiple classes
Verifying calls
Sentinels
Let’s start with a couple of classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from mock import Mock , patch
class KungFuPanda :
def __init_ ( self ):
self . secret_ingredient = "passion"
def foo ( self ):
super_long_sleep ()
return "soup"
def bar ( self , instructor ):
if instructor . message == "punch" :
return "hug"
else :
return "whatever"
def hello ( self ):
return "ahoy!"
class Instructor :
tribe = "know_it_all"
def __init__ ( self , name ):
self . name = name
def message ( self , message ):
self . message = speak_and_get_message_from_the_entire_community ()
@classmethod
def get_tribe ( cls ):
return cls . tribe
Mock classes
A KungFuPanda performs two actions – foo and bar. We want to test the output of the bar action when it gets a message from an instructor. However, the instructor needs to speak to the entire community before coming up with a message to test the KungFuPanda. We decide to mock the instructor class and quickly test the panda.
1
2
3
4
5
6
7
8
9
10
11
In [ 2 ]: kf = KungFuPanda ()
In [ 3 ]: mock_instructor = Mock ( message = "punch" )
In [ 4 ]: kf . bar ( mock_instructor )
Out [ 4 ]: 'hug'
In [ 5 ]: mock_instructor . message = "kill"
In [ 6 ]: kf . bar ( mock_instructor )
Out [ 6 ]: 'whatever'
Mock class methods
The instructors comes from the know_it_all tribe. What if we wanted to have fun at their expense? Mocking class methods does the trick.
1
2
3
4
@patch.object ( Instructor , 'get_tribe' )
def tribe_fun ( mock_get_tribe ):
mock_get_tribe . return_value = "simpleton"
return Instructor . get_tribe ()
Let the fun begin!
1
2
3
4
5
In [ 8 ]: Instructor . get_tribe ()
Out [ 8 ]: 'know_it_all'
In [ 9 ]: tribe_fun ()
Out [ 9 ]: 'simpleton'
Mock instances
I love the way the KungFuPandas say ahoy! when greeted. But let’s get a non-pirate greeting from them by creating mock versions.
1
2
3
4
5
@patch ( __name__ + '.KungFuPanda' )
def hello_panda ( MockKungFuPanda ):
instance = MockKungFuPanda . return_value
instance . hello . return_value = "hi there"
return KungFuPanda () . hello ()
Let’s say hello.
1
2
3
4
5
In [ 11 ]: KungFuPanda () . hello ()
Out [ 11 ]: 'ahoy!'
In [ 12 ]: hello_panda ()
Out [ 12 ]: 'hi there'
Mock instance methods
Let’s mock the hello instance method for fun.
1
2
3
4
5
6
In [ 13 ]: kf_mocking_panda = KungFuPanda ()
In [ 14 ]: kf_mocking_panda . hello = Mock ( return_value = "mock you!" )
In [ 15 ]: kf_mocking_panda . hello ()
Out [ 15 ]: 'mock you!'
Configurable return values
Suppose a panda wanted to fake it’s behavior after receiving instruction, it could use side_effects to mock the behavior.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [ 5 ]: def good_behavior_side_effect ( args ):
... : return good_behavior [ args ]
... :
In [ 6 ]: good_behavior = { "punch" : "punching!" , "punch_some_more" : "punching again!" }
In [ 7 ]: obedient_pandas = Mock ()
In [ 8 ]: obedient_pandas . bar . side_effect = good_behavior_side_effect
In [ 9 ]: obedient_pandas . bar ( "punch" )
Out [ 9 ]: 'punching!'
In [ 10 ]: obedient_pandas . bar ( "punch_some_more" )
Out [ 10 ]: 'punching again!'
In [ 11 ]: obedient_pandas . bar ( "punch_again" )
KeyError : 'punch_again'
The obedient_panda can also mock a sequence of behaviors when asked to foo .
1
2
3
4
5
6
7
In [ 12 ]: obedient_pandas . hello . side_effect = [ "hello" , "hello again!" ]
In [ 13 ]: obedient_pandas . hello ()
Out [ 13 ]: 'hello'
In [ 14 ]: obedient_pandas . hello ()
Out [ 14 ]: 'hello again!'
Restricted API for mock objects
The Instructors find it acceptable to be mocked (only for testing!) but don’t like extending their API even during testing. For example, giving them a name as shown below is unacceptable
1
2
3
4
5
6
7
8
9
In [ 15 ]: stupid_instructor = Mock ()
In [ 16 ]: stupid_instructor . name . return_value = "dimwit"
In [ 17 ]: stupid_instructor . name
Out [ 17 ]: < Mock name = 'mock.name' id = '4350823824' >
In [ 18 ]: stupid_instructor . name ()
Out [ 18 ]: 'dimwit'
Ok, let’s try to be respectful of the Instructor API by using auto_spec .
1
2
3
4
5
6
7
In [ 24 ]: from mock import create_autospec
In [ 28 ]: no_named_instructor . message ( "hello" )
Out [ 28 ]: < MagicMock name = 'mock.message()' id = '4350658000' >
In [ 29 ]: no_named_instructor . name . return_value = "dimwit"
AttributeError : Mock object has no attribute 'name'
Magic Mock
MagicMock provides default implementations for several magic methods.
1
2
3
4
5
6
In [ 30 ]: from mock import MagicMock
In [ 31 ]: some_mock = MagicMock ()
In [ 34 ]: len ( some_mock ), int ( some_mock ), float ( some_mock ), list ( some_mock )
Out [ 34 ]: ( 0 , 1 , 1.0 , [])
Mock multiple classes
Mocking multiple classes is straightforward to do but I often get the nesting order wrong for the patch decorators. Let’s mock both KungFuPanda and Instructor classes.
1
2
3
4
5
6
7
8
9
@patch ( __name__ + '.KungFuPanda' )
@patch ( __name__ + '.Instructor' )
def test_mock_multiple_classes ( MockInstructor , MockPanda ):
MockPanda . name = "peace-loving kung fu panda"
MockInstructor . name = "The Great"
print KungFuPanda . name , " is coached by " , Instructor . name
In [ 40 ]: test_mock_multiple_classes ()
peace - loving kung fu panda is coached by The Great
Verifying calls
It’s easy to know all the calls made using mock. I have shown an example using the call_args list option that I use frequently. For more options, please see the documentation .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In [ 77 ]: knowing_panda = KungFuPanda ()
In [ 78 ]: knowing_panda . bar = Mock ()
In [ 79 ]: knowing_panda . bar . return_value = "fake_bar"
In [ 80 ]: knowing_panda . bar ( "message" )
Out [ 80 ]: 'fake_bar'
In [ 81 ]: knowing_panda . bar ( "message_2" )
Out [ 81 ]: 'fake_bar'
In [ 82 ]: knowing_panda . bar ( "message_3" )
Out [ 82 ]: 'fake_bar'
In [ 83 ]: knowing_panda . bar . call_args_list
Out [ 83 ]: [ call ( 'message' ), call ( 'message_2' ), call ( 'message_3' )]
In [ 84 ]: knowing_panda . bar . call_args
Out [ 84 ]: call ( 'message_3' )
Sentinels
Finally, sentinels can be used to return uniquely named objects.
1
2
3
4
5
6
7
8
9
10
In [ 45 ]: from mock import sentinel
In [ 46 ]: instructor = Instructor ( "The Instructor" )
In [ 47 ]: instructor . message = Mock ()
In [ 48 ]: instructor . message . return_value = sentinel . fake_message
In [ 49 ]: instructor . message ()
Out [ 49 ]: sentinel . fake_message
Happy mocking!