How to mock things

Writing code you might soon get into a situation where you cannot easily test all code because of a not given environment where you can use it. While unittesting you should not connect to a database, to web services and avoid OS operations; that's more a topic for regression tests. Writing regressions tests and installing and configuring a test system are another story.

After here we start with providing a few functions for demonstrating the way to mock them. Any comments or improvements? Please let me know: thomas.lehmann.private@gmail.com

Please note: In this article we do not implement unit-tests so please do not worry when using assert here.


In [6]:
def low_level_foo(*args, **kwargs):
    """ simulates something like a low level OS call. """
    print("low_level_foo is called with %s and %s" % (args, kwargs))
    return args, kwargs

def high_level_foo(*args, **kwargs):
    """ simulates any kind of high level function. """
    print("high_level_foo is called with %s and %s" % (args, kwargs))
    value = low_level_foo(*args, **kwargs)
    #low_level_foo(*args, **kwargs)
    return value

# normal call:
high_level_foo(1, 3.1415, "hello", name="Barney")


high_level_foo is called with (1, 3.1415, 'hello') and {'name': 'Barney'}
low_level_foo is called with (1, 3.1415, 'hello') and {'name': 'Barney'}
Out[6]:
((1, 3.1415, 'hello'), {'name': 'Barney'})

Mock of a function (calls and parameters)

You can check whether all parameters which are passed correctly to a low level function call. Try to change a parameter in the first assert to see detailed explanation in the thrown assertion how the low function is called. Also uncomment the second low level call to the in the thrown assertion how often the low level function has been called.


In [7]:
from mock import patch, call

with patch("__main__.low_level_foo") as mocked_function:
    # now the low level call is patched:
    high_level_foo(1, 3.1415, "hello", name="Barney")
    mocked_function.assert_called_with(1, 3.1415, "hello", name="Barney")
    mocked_function.assert_called_once_with(1, 3.1415, "hello", name="Barney")


high_level_foo is called with (1, 3.1415, 'hello') and {'name': 'Barney'}

Mock of a function (return value)

Mocking a function is done as usual but here we use return_value to make the mock returning something other than None since a mock function (or method) doesn't implement anything. You see that the function call remains unchanged. Since Python is not type safe ensure that you return a value that is compatible to the original implementation. Here it's not a problem because the result of the low level call is returned and not used (Anyway we return a string but the original implementation returns a tuple and a dictionary).


In [19]:
with patch("__main__.low_level_foo") as mocked_function:
    print(high_level_foo(1, 2, 3, 4))
    mocked_function.return_value = "hello world"
    print(high_level_foo(1, 2, 3, 4))


high_level_foo is called with (1, 2, 3, 4) and {}
<MagicMock name='low_level_foo()' id='140057798876752'>
high_level_foo is called with (1, 2, 3, 4) and {}
hello world

Mocking of an object

Next we intend to mock an object. Therefore we provide two test classes. The class Foo is using and delegating calls to the class Bar. Assume that class Bar represents something like a client connection, a database or a file.


In [2]:
class Bar(object):
    def __init__(self, value):
        self.value = value
    def test1(self):
        print("Bar.test1() called; current value is %s" % self.value)
    def test2(self, value):
        print("Bar.test2(%s) called; previous value was %s" % (value, self.value))
        self.value = value

class Foo(object):
    def __init__(self, value):
        self.bar = Bar(value)
    def test1(self):
        print("Foo.test1() called")
        self.bar.test1()
    def test2(self, value):
        print("Foo.test2(%s) called" % value)
        self.bar.test2(value)

foo = Foo("hello world")
foo.test1()
foo.test2("sunny days")


Foo.test1() called
Bar.test1() called; current value is hello world
Foo.test2(sunny days) called
Bar.test2(sunny days) called; previous value was hello world

Using the assert functions it does work for the call of the constructor (init) but - as it seems - not for the methods. Of course all calls are tracked and listed in the field mock_calls. A way to assert that a function is called you can do with the call class. I just wonder why that has not been done more easy with standard interface "assert_whatever(param1, .., paramn)". However you can do the same as we did with the functions (above) with a method but then you would have to patch each individual method which is not comfortable.


In [8]:
with patch("__main__.Bar") as mocked_object:
    foo = Foo("hello world")
    mocked_object.assert_called_with("hello world")
    mocked_object.assert_called_once_with("hello world")
    foo.test1()
    print(mocked_object.mock_calls)
    # I would have prefered to use: mocked_object.test1.assert_called_with()
    # but unfortunately it doesn't work this way.
    assert call().test1() in mocked_object.mock_calls
    foo.test2("sunny days")
    print(mocked_object.mock_calls)
    assert call().test2("sunny days") in mocked_object.mock_calls


Foo.test1() called
[call('hello world'), call().test1()]
Foo.test2(sunny days) called
[call('hello world'), call().test1(), call().test2('sunny days')]

Custom mock class

There might be cases where you have to implement a custom mock class. In this case you cannot use the assert function since it's not a mock class from the library but the purpose is here a different one. You don't wanna test the way the class Bar is used but to ensure that the mock for the Bar class keeps the program in a valid state without using external resources. Assume Bar reads data from file or from a database caching the data the mock variant could simply have the cache (empty) and while testing you fill in the cache to do specific tests.


In [90]:
class BarMock(object):
    def __init__(self, value):
        self.value = value
    def test1(self):
        print("BarMock.test1() called; current value is %s" % self.value)
    def test2(self, value):
        print("BarMock.test2(%s) called; previous value was %s" % (value, self.value))
        self.value = value

with patch("__main__.Bar", new=BarMock) as mocked_object:
    foo = Foo("hello world")
    foo.test1()
    foo.test2("sunny days")


Foo.test1() called
BarMock.test1() called; current value is hello world
<__main__.BarMock object at 0x7f61cc072d10>
Foo.test2(sunny days) called
BarMock.test2(sunny days) called; previous value was hello world

In [ ]: