[Python] That's it! A summary of pytest mocks!

Hello!
This is Fukui from the Systems Development Department!

Since the year 2025, when I'm writing this blog, is the Year of the Snake in the Chinese zodiac,
, a Python testing framework pytest'd like to introduce you to mocks in

Although the title says "Mock Summary," it is narrowed down to functions and uses that the author has chosen based on his own personal experience and bias, and that he considers to be "essential to know."

So, I hope this will be of some help to those who have not yet used pytest or those who plan to use it in the future

Introduction

[Introduction]

As a prerequisite, this blog will primarily pytest-mock present code using the
the standard Python library's unittest.mock Please note that the syntax differs from that of
This blog unittest.mock ; for more information, please refer to the official Python documentation below:
https://docs.python.org/3/library/unittest.mock.html

[For those who have not used pytest yet]

I will also write some sample test code below, but if you have not used pytest before, I would appreciate it if you would read it with the following content in mind

The following is an example of a mock implemented in a test method, butthe `mocker`argumenta fixture provided by pytest-mockis
Therefore, if pytest-mock is installed, you can use it without needing to import anything.

def test_get_name(mocker): mocker.patch.object( (omitted) )

This blog does not explain "What is a fixture?" or "What is a mocker?" (about the actual workings of mockers). If you are interested, please refer to the official documentation for pytest and pytest-mock.
https://docs.pytest.org/en/stable/
https://pypi.org/project/pytest-mock/

Mocking methods and properties

Now, let's get straight to the main topic.
First, I will explain how to mock methods and properties.

Mocking instance methods

First, let's look at mocking instance methods, which are likely to be used in many situations.
For example, if you have a class called UserInfo and it has a method called get_name() that returns a value of type str, an example implementation would be as follows.

def test_get_name(mocker): test_name = "Test name" # Mock of get_name() of UserInfo class mocker.patch.object( UserInfo, "get_name", return_value=test_name )

the targetclass object or methodYou specify
if you want to specify a return value, the value of `return_value`you specify

Mocking a method in a specific module

If you want to mock a method that is not defined in the class, you can also mock it by specifying the module as shown below

def test_get_name(mocker): test_name = "Test name" # Mock get_name() from the app.services.user module mocker.patch( "app.services.user.get_name", return_value=test_name )

the targetmodule and the methods executed within that module as stringsYou can mock a module by specifying
When implementing this, be careful to specify the module correctly.

Mocking the same method by specifying different return values ​​when it is executed multiple times

There may be cases where the method under test executes the same method multiple times.
If a fixed return value is acceptable, you can implement the test using the method described above. However, depending on the process, the execution route may change depending on the return value.

That's where `side_effect` comes in handy. An implementation example is shown below.

def test_get_name(mocker): test_name_1 = "Ichiro Tanaka" test_name_2 = "Jiro Suzuki" # Mock of get_name() of UserInfo class mocker.patch.object( UserInfo, 'get_name', side_effect=[ test_name_1, # Return value when executed the first time test_name_2, # Return value when executed the second time ] )

side_effect` In short, `
Returning to the main topic, if you want to specify different values ​​for each execution count,by specifying the values ​​in the order you want them to be returned as a list,you can set it
`side_effect` later, and it is a convenient feature that can be used in a variety of ways.

Mocking Properties

If you want to mock not only methods but also properties,PropertyMock you can do so using
For example, if the UserInfo class has a property called first_name that stores the name, the implementation would look like this:

def test_first_name(mocker): first_name_taro = "Taro" # Mock the first_name property of UserInfo mocker.patch.object( UserInfo, "first_name", new_callable=PropertyMock, return_value=first_name_taro )

PropertyMock itself is a class provided by the Python standard library unittest.mock, and is used to mock the properties of a specific class, as in this case. (It is different from the Mock class used for regular mocking.)
The code itself is not that different from when mocking a method, butspecify new_callable, like new_callable=PropertyMocknote that you need to

Execute mock verification

Up to this point, we've discussed how to mock methods and properties. However,
when actually implementing unit tests,whether the mocked processes are being called as expected you'll likely want to verify
From here on,the execution of mocked methodsverify we'll discuss how to

Verify that it was executed

First, let's look at how to verify whether a method was executed or not.
.whether it was called onceIf you simply want to verifythe assert_called_once() you can implement this using
An example implementation is shown below

def test_get_name(mocker): test_name = "Test name" # Mock of get_name() of UserInfo class mock_get_name = mocker.patch.object( UserInfo, "get_name", return_value=test_name ) (※Method execution processing, etc.) # The mock of get_name() was executed once mock_get_name.assert_called_once()

, assuming it will be executed multiple timesif you want to verify the number of times a function is called,
as shown below the assert and call_count you can implement this by using

def test_get_name(mocker): test_name = "Test name" # Mock of get_name() of UserInfo class mock_get_name = mocker.patch.object( UserInfo, "get_name", return_value=test_name ) (※Method execution processing, etc.) # The mock of get_name() was executed twice assert mock_get_name.call_count == 2

 

Verify that the execution was performed with the expected arguments

Nextwhether the arguments passed to a mocked method are as expectedwe'll discuss how to verify
This`assert_called_with()` can be implemented using the
An example implementation is shown below.

def test_get_name(mocker): test_name = "Test name" # Expected value of argument passed to mocked method expected_user_id = 123 # Mock of get_name() of UserInfo class mock_get_name = mocker.patch.object( UserInfo, "get_name", return_value=test_name ) (※Method execution processing, etc.) # The argument passed to get_name() is the expected value (expected_user_id) mock_get_name.assert_called_with(user_id=expected_user_id)

Verify "whether it was executed" and "arguments" together

Up to this point, we've shown how to verify "whether the function was executed" and "whether the arguments were as expected" separately, but there's also a way to verify them together.
assert_called_once_with() method, you can perform these verifications without writing separate code.
An implementation example is shown below.

def test_get_name(mocker): test_name = "Test name" # Expected value of argument passed to mocked method expected_user_id = 123 # Mock of get_name() of UserInfo class mock_get_name = mocker.patch.object( UserInfo, "get_name", return_value=test_name ) (※Method execution processing, etc.) # The argument passed to get_name() is the expected value (expected_user_id) and has been executed once mock_get_name.assert_called_once_with(user_id=expected_user_id)

If you expect the mocked method to be called only once,`assert_called_once_with()` rather than assert_called_with()` it's better to use

the mocked methodto be called multiple timeseven ifassert_has_calls() you can use
An implementation example is shown below.

def test_get_name(mocker): test_name_1 = "Ichiro Tanaka" test_name_2 = "Jiro Suzuki" # Expected values ​​of arguments passed to the mocked method expected_user_id_tanaka = 1 expected_user_id_suzuki = 2 # Mock of get_name() of UserInfo class mock_get_name = mocker.patch.object( UserInfo, 'get_name', side_effect=[ test_name_1, # Return value when executed the first time test_name_2, # Return value when executed the second time ] ) (※Method execution processing, etc.) # Ensure that get_name() is executed in order with the expected arguments mock_get_name.assert_has_calls([ mocker.call(expected_user_id_tanaka), mocker.call(expected_user_id_suzuki) ])

Get the arguments passed to a mocked method

Additionally, although it's not a validation method like the one described above,retrieve the arguments passed to the mocked methodyou can also
of the mock object call_args.args` This can be done using the `
The retrieved value is of type `tuple` andcan be obtained as positional arguments.
An implementation example is shown below.

def test_get_name(mocker): test_name = "Test name" # Mock of get_name() of UserInfo class mock_get_name = mocker.patch.object( UserInfo, "get_name", return_value=test_name ) (※ Method execution processing, etc.) # Get arguments passed to the mocked method args = mock_get_name.call_args.args # First argument args_1 = args[0] # Second argument args_2 = args[1]

If you can retrieve the arguments, you can use assert to verify them (although you might not necessarily need to use assert), and
you can also write other processes using the retrieved arguments, so I personally thought it was worth remembering and introduced it here.
However, pleasethat call_args can only retrieve the arguments from the last execution, soif you want to retrieve the arguments of a method that is executed multiple times, you should use call_args_list.

Raising an exception using side_effect

Finally, let's look at how to raise an exception using a mock object.
, which we also discussed in another topic earlier `side_effect` we'll use
An example implementation is shown below.

def test_get_name(mocker): test_error_message = "The target user does not exist" # Raise an exception with get_name() of the UserInfo class mock_get_name = mocker.patch.object( UserInfo, "get_name", side_effect=Exception(test_error_message) )

The implementation of this method is also simple;specify the exception class instance you want to raise for `side_effect`you just need to
If the method being tested performs some kind of processing when an exception occurs, you can use this method to route the test through the exception handling route and increase test coverage.

summary

How was it?
In this blog, we introduced methods and implementations categorized by their use case, but I think all of them were simple and easy to implement.
Also, as I mentioned at the beginning, this content is limited to only what I personally think is worth remembering, so conversely, there are still many more features and methods that I haven't introduced here. If
you are interested, please refer to the official documentation to find better implementations and useful features! That's all
for today! See you next time!

If you found this article helpful,please give it a "Like"!
7
Loading...
7 votes, average: 1.00 / 17
3,037
X Facebook Hatena Bookmark pocket

The person who wrote this article

About the author

Hiroto Fukui

I joined Beyond in June 2020. I work in the Systems Development Department (Yokohama office).
My work mainly involves PHP, and I am in charge of developing game APIs, web systems, and Shopify private apps.
I like music in general, mainly Western music, and I play the guitar as a hobby. My favorite TV programs are "Detective! Night Scoop" and "Appearance! Admatic Heaven".