Using Python Mock to Haunt Your Code

Robert Roskam
Oct 24, 2016 · 3 min read
OK, this title is a bit of stretch. But I wanted so badly to make this in a Halloween theme.

Mock is awesome. Basically you can use it to override namespaces with whatever you want. I’ve often used it before. If you want a simple explanation, Fotis Gimian had a very helpful post a few years back.

So with that in mind, I want to share 2 specific use cases that I recently encountered.

Case 1: Testing without calling a REST/SOAP API

Please concede for me for a moment that there an exists some RESTful API in the universe that will double the money you give it. Let’s further say that it uses tokens for security. And I want to wrap up that API call and token in one spot in my code. I might write something like below. Let me further assume that the fantastic_api library is basically just a wrapper around requests, and so all calls return a requests object.

# money.pyimport fantastic_apidef double(money):
if not isinstance(money, numbers.Number):
raise Exception('Can only double numbers')
token = 'shhhh-ima-secret'
return fantastic_api.double_my_money(token, money)

I’d like some simple unit tests around this code. As desirable it may be to be calling this double_my_money api as often as possible, I should probably not call this API while testing. Just in case.

Here’s how to do that.

# tests.py
import
mock
from unittest import TestCase
import moneyclass ExampleTests(TestCase): @mock.patch('fantastic_api.double_my_money', return_value='5')
def test_double_money(self):
self.assertEquals(money.double(2), 5)

@mock.patch('fantastic_api.double_my_money', return_value=None)
def test_double_money_wrong_type(self):
self.assertRaises(Exception, money.double('apples')

So the idea here is pretty simple: I have the mock library overriding fantastic_api.double_my_money and stating the return_value. The test_double_money is just to ensure that indeed my override works as intended. (5 clearly is not double of 2, but I overrode it right here.)

The second test is the real test: I just want to look before I leap at run time, because the api library doesn’t have type checking itself. (I know kinda weird. Maybe fantastic_api isn’t so fantastic at all.)

Case 2: Pretend that you have imported a library

Let’s continue my previous example above. But let’s say that I’m trying to generalize my python script now to potentially use a lot of different services.

# money.pydef double(token, money):
if not isinstance(money, numbers.Number):
raise Exception('Can only double numbers')
_double = find_call()
return _double(token, money)
def find_call()
try:
import fantastic_api
return fantastic_api.double_my_money
except:
pass
try:
import awesome_api
return awesome_api.double
except:
raise Exception('Need to use awesome_api or fantastic_api')

Now we have a more generalized interface to be able use different API services to do the same thing. I just need to install one. That’s cool. But how do we test that?

# tests.py
from
mock import patch, MagicMock
from unittest import TestCase
import money
import fantastic_api
class ExampleTests(TestCase):

@patch.dict('sys.modules', {'awesome_api': MagicMock()}):
def test_double_money_wrong_type(self):
foo = money.find_call()
self.assertEquals(#something here that makes sense)

The biggest thing to note in all of this is that line with MagicMock. Basically, at the time of running this specific unit test, there will exist a module by the name of awesome_api.

This can be super useful if you have logic in your system that stops a user of a library from proceeding until they configure their code further.

Robert Roskam

Written by

I write about Python, Django, and web dev in general.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade