Python tutorials > Testing > Mocking > How to assert mock calls?

How to assert mock calls?

Mocking is a powerful technique in Python testing that allows you to isolate the unit under test by replacing its dependencies with controlled substitutes, called mocks. Asserting mock calls is crucial to verify that your code interacts with these dependencies as expected. This tutorial will guide you through various ways to assert mock calls using the unittest.mock library.

Basic Assertion: assert_called()

The assert_called() method simply checks if the mock object was called at least once. In the example, we create a mock dependency, pass it to my_function, and then assert that the do_something method of the mock was called. This is the most basic assertion you can make on a mock.

import unittest
from unittest.mock import Mock

def my_function(dependency):
    dependency.do_something(1, 2)

class TestMyFunction(unittest.TestCase):
    def test_my_function_calls_dependency(self):
        mock_dependency = Mock()
        my_function(mock_dependency)
        mock_dependency.do_something.assert_called()

Checking Call Arguments: assert_called_with()

The assert_called_with() method verifies that the mock was called exactly once with the specified arguments. In this example, we assert that do_something was called with the arguments 1 and 2.

import unittest
from unittest.mock import Mock

def my_function(dependency):
    dependency.do_something(1, 2)

class TestMyFunction(unittest.TestCase):
    def test_my_function_calls_dependency_with_correct_arguments(self):
        mock_dependency = Mock()
        my_function(mock_dependency)
        mock_dependency.do_something.assert_called_with(1, 2)

Checking Call Order and Multiple Calls: assert_has_calls() and call

The assert_has_calls() method checks if the mock was called with a sequence of specific calls in the given order. The call object represents a single call to the mock. This example verifies that do_something was called first with 1 and then with 2.

import unittest
from unittest.mock import Mock, call

def my_function(dependency):
    dependency.do_something(1)
    dependency.do_something(2)

class TestMyFunction(unittest.TestCase):
    def test_my_function_calls_dependency_multiple_times(self):
        mock_dependency = Mock()
        my_function(mock_dependency)
        mock_dependency.do_something.assert_has_calls([call(1), call(2)])

Checking Any Call: any

The any_ object can be used as a placeholder argument when you don't care about the specific value of that argument. This example asserts that do_something was called with 1 as the first argument and any value as the second argument.

import unittest
from unittest.mock import Mock, any_

def my_function(dependency):
    dependency.do_something(1, 'hello')

class TestMyFunction(unittest.TestCase):
    def test_my_function_calls_dependency_with_any(self):
        mock_dependency = Mock()
        my_function(mock_dependency)
        mock_dependency.do_something.assert_called_with(1, any_)

Checking the Most Recent Call: assert_called_once_with()

The assert_called_once_with() method asserts that the mock was called exactly once with the specified arguments. It's different from assert_called_with if you only want to ensure that the function was only called once. The previous calls won't make the validation fail. In the code, an AssertionError is raised because do_something was called twice.

import unittest
from unittest.mock import Mock

def my_function(dependency):
    dependency.do_something(1)
    dependency.do_something(2)

class TestMyFunction(unittest.TestCase):
    def test_my_function_calls_dependency_once_with(self):
        mock_dependency = Mock()
        my_function(mock_dependency)
        with self.assertRaises(AssertionError):
            mock_dependency.do_something.assert_called_once_with(1)

Concepts Behind the Snippet

Mocking is a testing technique that replaces real dependencies of a unit under test with controlled substitutes (mocks). Assertions on mock calls verify that the unit under test interacts with its dependencies as expected. These assertions provide confidence that the code is functioning correctly by isolating and testing its interactions with external components.

Real-Life Use Case

Consider a scenario where you're testing a function process_data that interacts with an external API client. You can mock the API client to control its behavior (e.g., return a success or failure response) and then assert that the process_data function calls the API client's methods (e.g., send_data, log_success, log_failure) with the correct arguments based on the API response.

import unittest
from unittest.mock import Mock

def process_data(api_client, data):
    result = api_client.send_data(data)
    if result['status'] == 'success':
        api_client.log_success(data)
    else:
        api_client.log_failure(data)

class TestProcessData(unittest.TestCase):
    def test_process_data_success(self):
        mock_api_client = Mock()
        mock_api_client.send_data.return_value = {'status': 'success'}
        
        process_data(mock_api_client, {'key': 'value'})
        
        mock_api_client.send_data.assert_called_with({'key': 'value'})
        mock_api_client.log_success.assert_called_with({'key': 'value'})
        mock_api_client.log_failure.assert_not_called()

    def test_process_data_failure(self):
        mock_api_client = Mock()
        mock_api_client.send_data.return_value = {'status': 'failure'}
        
        process_data(mock_api_client, {'key': 'value'})
        
        mock_api_client.send_data.assert_called_with({'key': 'value'})
        mock_api_client.log_success.assert_not_called()
        mock_api_client.log_failure.assert_called_with({'key': 'value'})

Best Practices

  • Be specific in your assertions: Avoid general assertions like assert_called() when you can be more precise with assert_called_with() or assert_has_calls().
  • Test only the unit under test: Ensure that your mocks only represent dependencies of the unit being tested. Avoid mocking internal implementation details.
  • Keep tests small and focused: Each test should verify a single aspect of the unit's behavior. This makes tests easier to understand and maintain.
  • Use descriptive test names: Name your tests in a way that clearly describes what is being tested.

Interview Tip

When discussing mocking in interviews, be prepared to explain:

  • The benefits of using mocks for isolating units of code.
  • The different types of mock call assertions and when to use each one.
  • How to handle complex scenarios involving multiple mock calls and return values.
  • The importance of clean and well-structured test code.

When to Use Mock Call Assertions

Use mock call assertions whenever you need to verify that your code interacts correctly with its dependencies. This is especially important when dealing with:

  • External APIs
  • Databases
  • File systems
  • Complex objects with side effects

Alternatives

While unittest.mock is the standard library's mocking framework, other popular options exist:

  • pytest-mock: A pytest plugin that simplifies mocking.
  • doublex: A mocking framework that emphasizes testability and code clarity.

Pros

  • Isolation: Mocks allow you to isolate the unit under test, making tests faster and more reliable.
  • Control: You can control the behavior of dependencies, simulating various scenarios and edge cases.
  • Testability: Mocking can make it easier to test code that is difficult or impossible to test directly.

Cons

  • Complexity: Mocking can add complexity to your tests, especially when dealing with complex dependencies.
  • Over-mocking: It's possible to over-mock, leading to tests that are tightly coupled to the implementation details and break easily when the code changes.
  • Maintenance: Mocks need to be maintained along with the code they are testing.

FAQ

  • What happens if I use assert_called_with when the mock was never called?

    An AssertionError will be raised, indicating that the mock was not called with the specified arguments.
  • Can I check the arguments passed to a mock when the order of arguments is not important?

    Yes, you can use call_args to get the arguments as a tuple or dictionary, and then use standard Python assertions to check the arguments regardless of order.
  • How can I assert that a mock method was not called?

    You can use assert_not_called(). For example: mock_object.method.assert_not_called()