Mocking#

Sometimes our programmes depend on external circumstances, but during unit testing we shouldn’t worry about whether they work. And only test our code. So there are ways to change the behaviour of functions called in the programs being tested, to make it convenient for us to do the testing.

from unittest import mock

Syntax#

There are several ways to perform mocking using the unittest library:

  • Defining a mock object: This creates an object for which you can specify custom behavior.

  • Patching existing functions or methods: This is often the most practical and widely used approach. There are several syntax options for patching:

    • Using a context manager (with): The mock applies only within the scope of the with block, allowing you to define test-specific behavior.

    • Using a decorator for a specific method: The decorated method will use the mock instead of the real object during execution.

    • Applying to an entire unittest.TestCase subclass: All calls to the patched object within the test case will be handled by the corresponding mock.

    Find out more in particular page.


The following cell shows what Mock is. Object for which you can define what it must return when called.

mock = unittest.mock.Mock()
mock.return_value = "Hello mock"
mock()
'Hello mock'

The following code shows the idea of the patch. The behaviour of my_fun inside the with block is changed according to the return_value attribute.

def my_fun(a, b):
    return "Original output"

print(my_fun(10, 20))

with unittest.mock.patch("__main__.my_fun") as mock_object:
    print(type(mock_object))
    mock_object.return_value = "Hello patch"
    print(my_fun())
Original output
<class 'unittest.mock.MagicMock'>
Hello patch

Note: The object returned by the context manager (mock_object) is of the unittest.mock.MagicMock type. You interact with it like any standard mock object, as it simply “translates” the defined behavior into the patched object.

Call details#

There are a number of tools that allow you to get details of the call to the mocker object. They are considered in the corresponding page.


The following cell creates mock and few calls for it.

mock = unittest.mock.Mock()
mock(10, 20, 30)
mock("hello")
call('hello')

Consider which information about actions with the mock object we can get.

The call_args file contains the values passed in the last call.

mock.call_args
call('hello')

mock_calls contains a list of objects describing all calls to the mocked object.

mock.mock_calls
[call(10, 20, 30), call('hello')]

There is a special attribute for number of calls of the mock - call_count.

mock.call_count
2

Dynamic attributes#

Mock object automatically attributes that program refers to at the some stage. So you can mock any object and consider how program refers to it at the different stages of the program.


Consider a real-world example - suppose you need to check which query was passed to the cursor of the database.

The following cell creates a mocked cursor mocked object and sets to its execute attribute (which actually wasn’t defined anywhere before) to return_value = "hello".

cursor = mock.Mock()
cursor.execute.return_value = "hello"
cursor.execute.mock_calls = []

The following cell calls the “method” we just defined.

cursor.execute("some input")
'hello'

It returns the value defined in the return_value argument. You can interact with cursor.exexcute just like a normal mock object. For example following cell shows details of the call we created earlier.

cursor.execute.mock_calls
[call('some input')]