Skip to content

Instantly share code, notes, and snippets.

@proteusvacuum
Created October 28, 2015 17:09
Show Gist options
  • Save proteusvacuum/ab6dbb95ed210fb55ada to your computer and use it in GitHub Desktop.
Save proteusvacuum/ab6dbb95ed210fb55ada to your computer and use it in GitHub Desktop.
Python mocks brownbag
# coding: utf-8
from mock import MagicMock, call, patch
# “mock is a library for testing in Python. It allows you to replace parts of
# your system under test with mock objects and make assertions about how they
# have been used.”
thing = MagicMock(name='thing', return_value=100)
print thing()
print thing(1, 2, 3)
print thing.call_count
print thing.called
print thing.assert_called_with(1)
print thing.assert_called_with(1, 2, 3)
print thing.call_args
print thing(1, 2, foo='bar')
print thing.call_args == call(1, 2, foo='bar')
thing.side_effect = Exception("BOOM!")
thing()
thing.side_effect = lambda *args, **kwargs: (args, kwargs)
print thing(1, 2, 3, foo="bar")
# foot shooting
# tied to implementation
# patch
# The patch decorators are used for patching objects only within the scope of
# the function they decorate. They automatically handle the unpatching for you,
# even if exceptions are raised. All of these functions can also be used in with
# statements or as class decorators
class Thinger(object):
def do_thing(self):
return 100
with patch('__main__.Thinger') as FakeThinger:
"""patch whole objects"""
thing = FakeThinger.return_value
thing.do_thing.return_value = 12
print Thinger().do_thing() # 12
print type(Thinger())
with patch('__main__.Thinger.do_thing') as fake_thing_doer:
"""patch object methods"""
fake_thing_doer.return_value = 12
print Thinger().do_thing()
print type(Thinger())
class MockThinger(object):
def do_thing(self):
return 100000
with patch('__main__.Thinger', new=MockThinger):
"""use a different object in place of the patched object"""
print Thinger().do_thing() # 100000
print type(Thinger())
# Can be used as a method decorator
@patch('__main__.Thinger')
def test_thinger_does_thing(self, fake_thinger):
pass
# Can also be used as a class decorator
@patch('__main__.Thinger')
class ThingerTest(SimpleTestCase):
def test_thinger_does_thing(self, FakeThinger):
print Thinger().do_thing()
# 10000
def test_other_thing_calls_do_thing(self, FakeThinger):
ThingerFactory().make_thing_do_thing()
self.assertEqual(25, Thing.do_thing.call_count)
with patch.object(Thinger, 'do_thing') as fake_thing_doer:
fake_thing_doer.return_value = 'foo'
print Thinger().do_thing()
lookup_table = {'foo': 'bar', 'baz': 'quux'}
with patch.dict(lookup_table, {'foo': 'biff'}):
print lookup_table['foo']
print lookup_table['baz']
# biff
# Where to patch!
# ===============
# The basic principle is that you patch where an object is
# looked up, which is not necessarily the same place as where it is defined
# Basically, we patch the object IN THE PLACE INTO WHICH IT HAS BEEN IMPORTED, not where it is defined
# >>> Want to test whether 'send_to_elastic' is called
# >>> File I am testing: corehq/apps/users/signals.py
from corehq.elastic import send_to_elasticsearch
def update_user_in_es(sender, couch_user, **kwargs):
"""
Automatically sync the user to elastic directly on save or delete
"""
send_to_elasticsearch("users", couch_user.to_json(),
delete=couch_user.to_be_deleted())
# >>> in test file
@patch('corehq.apps.users.signals.send_to_elasticsearch')
def test_send_to_elastic_is_called(self, send_to_es):
do_a_thing()
self.assertTrue(send_to_es.called)
# Patch dates!
# No more randomness!
from datetime import date
with patch('mymodule.date') as mock_date:
mock_date.today.return_value = date(2015, 10, 28)
mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
my_module.do_a_thing_with_today() # doesn't matter when this test is run!
assert mymodule.date.today() == date(2010, 10, 8)
assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
# Fake Couch
# OMG we can fake couch!
# can use the @mock_out_couch decorator in util/test_utils.py
# ## blanket mocks out couch
# e.g. users/tests/test_signals.py
# or:
# can pass JSON into FakeCouch which returns whatever you want
# Homework: learn how to use it
# ## OMG mocks r sooo cooool.
# Chill: Too many mocks can happen!
# General rule is to use mocks across architecture boudaries (e.g. don't care if db is saving properly,
# don't want to call external apis)
# ## Resources
# http://martinfowler.com/articles/mocksArentStubs.html
# https://www.youtube.com/watch?v=yFA-FFaEZPo
# http://www.voidspace.org.uk/python/mock/patch.html
# http://www.voidspace.org.uk/python/mock/examples.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment