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")
Out[6]:
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")
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))
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")
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
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")
In [ ]: