DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

2

python: unit test with fixture and patch decorators

In the [pervious article], I used multiple patch decorators to mock several functions. This time, I use Fixture with the decorators to see how they work together.

Fixture

When I have a reusable object across multiple unit tests, I can define a fixture and obtain it in the unit test function. I create one fixture this time to see how I can use it.

Structures and code

This is almost same as before, just modifying add_data and main method to take an argument, and

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

my.py

import sys
from my_modules.util import util, get_data


def main(argv: list[str]):
    data = get_data(argv)
    return util(data)

if __name__ == '__main__':
    main(sys.argv)
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(input: list[str]) -> str:
    input = f"There are {len(input)} arguments"
    return add_time(input)
Enter fullscreen mode Exit fullscreen mode

Add fixture

I can define fixture in conftest.py that automatically recognize via test frameworks. I define a list_mock fixture this time.

conftest.py

import pytest

@pytest.fixture
def list_mock():
    return ["input1", "input2"]
Enter fullscreen mode Exit fullscreen mode

Add unit test for get_data

Let's add or modify the unit test code for get_data.

@patch('my_modules.util.add_time')
def test_get_data(add_time, list_mock):
    ct = datetime.now()
    expected = f"{ct}: There are {len(list_mock)} arguments"
    add_time.return_value = expected

    result = get_data(list_mock)

    assert expected == result
Enter fullscreen mode Exit fullscreen mode

I patch the add_time function and receive the mock as the first argument of the test function. Then, I receive the list_mock fixture as the second argument.

I can specify the fixture function name as argument name, then it is automatically passed to the function. Very easy!!

Update main unit tests

As the main method also requires list[str] argument, let's update them all. Just receive the list_mock fixture as an argument and pass it to the main function.

test_my.py

from my import main
from unittest.mock import patch, Mock

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

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

def test_main_util_called_with_expected_parameter_with(list_mock):
    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(list_mock)
    assert result =='dummy'
    util_mock.assert_any_call('some data')


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

Summary

I defined


 as a fixture this time, but I can use any object including ``Mock`` object. When I write similar test objects in multiple unit test, we can move it to fixture.

See [the pytest fixtures official document](https://docs.pytest.org/en/7.1.x/how-to/fixtures.html) for more detail.
Enter fullscreen mode Exit fullscreen mode

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

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

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

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

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay