简体   繁体   中英

Check if pytest fixture is called once during testing

Does pytest provides functionality like unittest.mock to check if the mock was actually called once(or once with some parameter)?

Sample Source code:

my_package/my_module.py

from com.abc.validation import Validation


class MyModule:
    def __init__(self):
        pass

    def will_call_other_package(self):
        val = Validation()
        val.do()

    def run(self):
        self.will_call_other_package()

Sample test code for the above source code:

test_my_module.py

import pytest
from pytest_mock import mocker

from my_package.my_module import MyModule

@pytest.fixture
def mock_will_call_other_package(mocker):
    mocker.patch('my_package.my_module.will_call_other_package')


@pytest.mark.usefixtures("mock_will_call_other_package")
class TestMyModule:

    def test_run(self):
        MyModule().run()
        #check `will_call_other_package` method is called.

        #Looking for something similar to what unittest.mock provide
        #mock_will_call_other_package.called_once

If you want to use a fixture that does the patching, you can move the patching into a fixture:

import pytest
from unittest import mock

from my_package.my_module import MyModule

@pytest.fixture
def mock_will_call_other_package():
    with mock.patch('my_package.my_module.will_call_other_package') as mocked:
        yield mocked
    # the mocking will be reverted here, e.g. after the test


class TestMyModule:

    def test_run(self, mock_will_call_other_package):
        MyModule().run()
        mock_will_call_other_package.assert_called_once()

Note that you have to use the fixture parameter in the test. Just using @pytest.mark.usefixtures will not give you access to the mock itself. You can still use it to be effective in all tests in the class, if you don't need to access the mock in all tests (or use autouse=True in the fixture).

Also note that you don't need pytest-mock here - but as mentioned by @hoefling, using it makes the fixture better readable, because you don't need the with clause:

@pytest.fixture
def mock_will_call_other_package(mocker):
    yield mocker.patch('my_package.my_module.will_call_other_package')

As an aside: you don't need to import mocker . Fixtures are looked up by name, and available automatically if the respective plugin is installed.

You could try this:

import pytest

from my_package.my_module import MyModule

def test_run(mocker):
    mocker.patch('my_package.my_module.will_call_other_package')
    MyModule().run()
    mock_will_call_other_package.assert_called_once()

First of all, you may not need the burden of an external library such as pytest_mock , because pytest already got you covered using theintegration with unittest .

You also do not need to use the usefixtures because whenever you need a fixture, you just receive it in your test method.

An ideal scenario based on your own code would look similar to this:

import pytest
from unittest.mock import patch

from com.abc.validation import Validation


class MyModule:
    def __init__(self):
        pass

    def will_call_other_package(self):
        val = Validation()
        val.do()

    def run(self):
        self.will_call_other_package()


@pytest.fixture
def call_other_module():
    with patch("my_package.my_module.MyModule.will_call_other_package") as _patched:
        yield _patched


class TestMyModule:
    def test_run_will_call_other_package(self, call_other_module):
        call_other_module.assert_not_called()
        obj = MyModule()
        call_other_module.assert_not_called()
        obj.run()
        call_other_module.assert_called_once()

And also if you want to make sure that you did infact patch the target MyModule.will_call_other_package , modify your test like this:

class TestMyModule:
    def test_run_will_call_other_package(self, call_other_module):
        call_other_module.assert_not_called()
        obj = MyModule()
        call_other_module.assert_not_called()
        obj.run()
        call_other_module.assert_called_once()
        assert False, (MyModule.will_call_other_package, call_other_module)

And you'll see something similar to this:

AssertionError: (<MagicMock name='will_call_other_package' id='140695551841328'>, <MagicMock name='will_call_other_package' id='140695551841328'>)

As you can see the id of both objects are the same, confirming our experiment was successful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM