【问题标题】:Check if pytest fixture is called once during testing检查是否在测试期间调用了一次 pytest 夹具
【发布时间】:2021-07-17 20:09:56
【问题描述】:

pytest 是否提供类似unittest.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

【问题讨论】:

  • 您可以像往常一样使用unittest.mockpytest。还有pytest-mock 插件提供mocker 固定装置,它基本上是unittest.mock 周围的薄包装。
  • 如果我使用unittest.mock,它会扼杀我开始使用固定装置的全部目的(重新使用模拟)你能告诉我一个如何使用@实现这一点的例子987654333@?
  • 您可以将模拟放入夹具中。我明天可以给你看一个例子,而不是现在在电脑上。

标签: python pytest pytest-mock


【解决方案1】:

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

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 不会让您访问模拟本身。如果您不需要在所有测试中访问模拟(或在夹具中使用autouse=True),您仍然可以使用它在类中的所有测试中有效。

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

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

顺便说一句:您不需要导入mocker。固定装置按名称查找,如果安装了相应的插件,则自动可用。

【讨论】:

  • mocker 的优点是它会在测试拆卸时自动恢复补丁,因此with 上下文变得不必要。 IMO 这使得测试的缩进更少并且更具可读性,但当然这最终是一个品味问题。
  • 谢谢,好点子 - 我调整了答案。
  • 谢谢,伙计们。这就是我一直在寻找的。​​span>
【解决方案2】:

你可以试试这个:

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

【讨论】:

  • 还有什么想法吗?
  • 为了使用mocker夹具,除了pytest之外,还必须安装pytest-mock
【解决方案3】:

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

您也不需要使用usefixtures,因为当您需要一个夹具时,您只需在您的测试方法中收到它。

基于您自己的代码的理想场景类似于:

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 相同,证明我们的实验是成功的。

【讨论】:

  • 您能解释一下这与我的回答有何不同吗?如果这个更好,我可以删除我的答案。
  • @MrBeanBremen 你可能是对的。我试着详细解释,但如果你认为它是多余的,让我们删除我的,因为你来得早。 ?
  • 没关系,让他们两个都保留 - 我只是想我错过了什么。
猜你喜欢
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
  • 2021-02-17
相关资源
最近更新 更多