DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

7 1

python: use multiple patch decorators to mock functions

I wrote how to mock in the previous article. This time, I mock multiple functions in the test to see how I can handle them.

Structures and code

This is almost same as before, just adding one more function in util.py.

src/
    ├── my.py  
    ├── my_modules/
    │   ├── __init__.py
    │   └── util.py
    └── tests/
        ├── __init__.py
        ├── test_my.py
        └── test_unit.py
Enter fullscreen mode Exit fullscreen mode

my.py

from my_modules.util import util, get_data

def main():
    data = get_data()
    return util(data)

if __name__ == '__main__':
    main()
Enter fullscreen mode Exit fullscreen mode

util.py

from datetime import datetime

def util(input: str) -> str:
    input = add_time(input)
    return f"util: {input}"

def add_time(input: str) -> str:
    return f"{datetime.now()}: {input}"

def get_data() -> str:
    return "Return some data"
Enter fullscreen mode Exit fullscreen mode

Add a unit test for main function

To test the main method in the my.py, I need to mock both util and get_data method. Let's do it.

Firstly, I added another patch and specify return_value. It doesn't change the test body though.

@patch("my.util", Mock(return_value="dummy"))
@patch("my.get_data", Mock(return_value="some data"))
def test_main():
    result = main()
    assert result =='dummy'
Enter fullscreen mode Exit fullscreen mode

Let's receive the mock objects as the arguments.

@patch("my.util")
@patch("my.get_data")
def test_main_util_called_with_expected_parameter(get_data_mock, util_mock):
    get_data_mock.return_value = 'some data'
    util_mock.return_value = 'dummy'
    result = main()
    assert result =='dummy'
    util_mock.assert_any_call('some data')
Enter fullscreen mode Exit fullscreen mode

The interesting part is the order of argument. As you see, I can get the mock from bottom up order of the patch decorators. I firstly though I can receive the mocks in the same order as the decorators, but I was wrong.

Finally, let's try with statement.

def test_main_util_called_with_expected_parameter_with():
    with patch("my.util") as util_mock:
        util_mock.return_value = 'dummy'
        with patch("my.get_data") as get_data_mock:
            get_data_mock.return_value = 'some data'
            result = main()
    assert result =='dummy'
    util_mock.assert_any_call('some data')
Enter fullscreen mode Exit fullscreen mode

I am not 100% sure if this is the correct way to implement, but it works as expected anyway.

I just added one more example. This time, I specify the Mock object in the first decorator only. In this case, I can receive just one mock object as an argument.

@patch("my.util", Mock(return_value="dummy"))
@patch("my.get_data")
def test_main_get_data_called(get_data_mock):
    get_data_mock.return_value = 'some data'
    result = main()
    assert result =='dummy'
    assert get_data_mock.called
Enter fullscreen mode Exit fullscreen mode

I check if get_data function is called.

Summary

I understand when I get the mock object as the arguments. It's a bit confusing but once I understand, it's very useful.

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay