Pytest - 如何断言一个函数是否调用了 monkeypatched 方法

Pytest - How to assert whether a function have called monkeypatched methods

我有一个调用许多其他第 3 方方法的复杂函数。我猴子一个一个地修补了它们:

import ThirdParty as tp

def my_method():
  tp.func_3rd_party_1()
  ...
  tp.func_3rd_party_5()

  return "some_value"

在我的测试中:

import pytest

def test_my_method(monkeypatch):
    monkeypatch.setattr(ThirdParty, 'func_3rd_party_1', some_mock_1())
    ...
    monkeypatch.setattr(ThirdParty, 'func_3rd_party_5', some_mock_5())

    return_value = my_method()
    assert return value

这运行得很好,但这种形式的测试对我来说太含蓄了。我想明确声明确实调用了 monkeypatched 方法。

郑重声明,我的模拟方法没有使用任何内置模拟库资源。它们只是重新定义的方法(智能存根)。

有什么方法可以断言吗?

因此专门提供了 pytest monkeypatching fixture,因此您可以更改一些全局属性,如环境变量、第三方库中的内容等,为您的测试提供一些可控且简单的行为。

另一方面,模拟对象旨在提供对对象的各种跟踪和检查。

两者齐头并进:您使用修补程序将某些第三方函数替换为 Mock 对象,然后执行您的代码,然后询问 Mock 对象是否确实已使用正确的参数调用它,因为正确的次数。

请注意,即使 mock 模块是 unittest 的一部分,它也可以与 pytest 完美配合。

现在至于补丁本身,这取决于你的个人喜好,并且有点取决于你想要补丁的具体内容,使用 unittest.mock.patch 更紧凑还是 pytest 的 monkeypatch fixture。

import pytest
from unittest.mock import Mock

def test_my_method(monkeypatch):
    # refer to the mock module documentation for more complex 
    # set ups, where the mock object _also_ exhibits some behavior.
    # as is, calling the function doesn't actually _do_ anything.
    some_mock_1 = Mock()
    ...
    some_mock_5 = Mock(return_value=66)

    monkeypatch.setattr(ThirdParty, 'func_3rd_party_1', some_mock_1)
    ...
    monkeypatch.setattr(ThirdParty, 'func_3rd_party_5', some_mock_5)

    some_mock_1.assert_called_once()
    some_mock_5.assert_called_with(42)
    ...

关于此类测试的注意事项:不要过火!它很容易导致所谓的 brittle 测试:只要对代码进行最细微的更改,测试就会中断。它可以使重构成为不可能的噩梦。

在以消息为中心的面向对象方法中使用这些类型的断言是最好的。如果被测class或方法的整个是以特定方式调用另一个对象的方法或class,则Mock away。另一方面,如果对第三方函数的调用只是达到目的的一种手段,那么在测试中进行更高级别的测试并测试所需的 行为