检查pytest fixture在测试期间是否调用过一次

2024-03-29 04:36:59 发布

您现在位置:Python中文网/ 问答频道 /正文

pytest是否提供类似于unittest.mock的功能来检查mock是否实际被调用过一次(或者使用某些参数调用过一次)

示例源代码:

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()

上述源代码的测试代码示例:

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


Tags: runfromimportselfpackagepytestmydef
3条回答

首先,您可能不需要像pytest_mock这样的外部库的负担,因为pytest已经使用integration with unittest覆盖了您

您也不需要使用usefixtures,因为每当您需要一个fixture时,您只需要在测试方法中接收它

基于您自己的代码的理想场景如下所示:

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()

另外,如果您想确保确实对目标MyModule.will_call_other_package进行了修补,请按如下方式修改您的测试:

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)

你会看到类似的东西:

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

正如您所看到的,两个对象的id是相同的,这证实了我们的实验是成功的

你可以试试这个:

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()

如果要使用进行修补的夹具,可以将修补移动到夹具中:

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()

请注意,您必须在测试中使用fixture参数。仅仅使用@pytest.mark.usefixtures将无法访问模拟本身。如果不需要在所有测试中访问mock(或者在fixture中使用autouse=True),那么仍然可以使用它在类中的所有测试中有效

还要注意,这里不需要pytest-mock,但正如@hoefling所提到的,使用它可以使fixture更可读,因为您不需要with子句:

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

顺便说一下:您不需要导入mocker。fixture是按名称查找的,如果安装了相应的插件,它将自动可用

相关问题 更多 >