In [ ]:
# EXERCISE:
# Execute nosetests in this folder:
!nosetests -sv
# Exercise: review tests_lib and make the tests to pass
# Use this cell
  • Regarding mocking, in Python monkey patching classes is trivial. It is really easy to replace instances methods or modules functions with your own stuff.
  • To use real full-featured mocks use the 'mock' library:

  • mock library allows to replace parts of our code in a safe and easy way with

    • mock objects. You can assert how they are called too.
  • Base class is Mock although is advisable to use MagicMock subclass
    • MagicMock has all "magic" methods already pre-created
  • patch utility allow to monkey patching at module and class level within the scope of test
  • Let's see a quick example

In [ ]:
from mock import MagicMock
from lib_to_test import ProductionClass

In [ ]:
from mock import MagicMock
from lib_to_test import ProductionClass
prod = ProductionClass()
prod.prod_method = MagicMock(return_value=3)
print prod.prod_method(40, 3)
prod.prod_method.assert_called_once_with(40, 3)

In [ ]:
# with side_effect we can return several values and raise exceptions too
prod.prod_method = MagicMock(side_effect=ValueError("not number"))
prod.prod_method("my_string")

In [ ]:
prod.prod_method = MagicMock(side_effect=[2, 3, 4])
prod.prod_method(34, 2)
prod.prod_method(34, 2)
prod.prod_method(34, 2)

In [ ]:
# But we are modifying our source code, we better use patch
from mock import patch

In [ ]:
import lib_to_test
# patch as decorator, provides MagicMock in function decorated
@patch('lib_to_test.ProductionClass')
def test(mockClass):
    lib_to_test.ProductionClass()  # Already imported in module..
    print 'ProductionClass {}'.format(lib_to_test.ProductionClass)
    assert mockClass is lib_to_test.ProductionClass
    print mockClass.called

In [ ]:
test()

In [ ]:
print lib_to_test.ProductionClass

In [ ]:
# We can also use patch for system libraries...
import sys

# patch as context manager
with patch('sys.exit') as exitMock:
    try:
        sys.exit()
    except SystemExit:
        print "exiting programm"
    exitMock.assert_called_once_with()

In [ ]:
try:
    sys.exit()
except SystemExit:
    print "exiting programm"

In [ ]:
# we can patch some object, dict, open and magic methods....

my_patch = patch.object(prod, 'prod_method')
method_mock = my_patch.start()
method_mock.return_value = 5000
print prod.prod_method(30, 3)

In [ ]:
# we stop the patch now ...
my_patch.stop()
print prod.prod_method
print prod.prod_method(30, 3)

In [ ]:
# WTF ??, yes before we change ProductionClass inside the module....
print prod.prod_method
prod = ProductionClass()
my_patch = patch.object(prod, 'prod_method')
method_mock = my_patch.start()
method_mock.return_value = 5000
print prod.prod_method(30, 3)
my_patch.stop()
print prod.prod_method(30, 3)

In [ ]:
# magic methods
mock = MagicMock()
mock.__str__.return_value = 'foobarbaz'
cad = str(mock)
mock.__str__.assert_called_with()
print cad

In [ ]:
# dictionary
foo = {'key': 'value'}
original = foo.copy()
with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
    print "dict foo is now: {}".format(foo)
    assert foo == {'newkey': 'newvalue'}

In [ ]:
print "dict foo is {}".format(foo)
assert foo == original

In [ ]:
# open function can be patched tooo....
from mock import mock_open

# write
with patch('__builtin__.open', mock_open()) as m:
    with open('foo.txt', 'w') as f:
        f.write('something')
    print m.mock_calls

In [ ]:
import os
os.path.exists('foo.txt')

In [ ]:
# read
with patch('__builtin__.open', mock_open( read_data='foo')) as m:
    with open('foo.txt') as f:
        file = f.read()
    print file

In [ ]:
!pip install nose-cov
!nosetests -s -v --with-cover --cover-package=lib_to_test