【问题标题】:Pytest - How to assert whether a function have called monkeypatched methodsPytest - 如何断言一个函数是否调用了猴子补丁方法
【发布时间】:2021-06-21 14:10:04
【问题描述】:

我有一个调用许多其他第 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方法。

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

有什么方法可以断言吗?

【问题讨论】:

  • 您不能直接询问模拟对象是否以您希望的方式调用它们吗?
  • some_mock_1.assert_called_once()assert_called 的任何变体。
  • @Lagerbaer 感谢您的回复!就这么简单吗?作为记录,我没有使用任何 Mock 对象,所以它们只是我纯粹重新定义的函数。
  • 哦,如果您使用自己的函数,那么assert_called() 等将不起作用;您必须从例如获取MockMagicMock unittest.mock.
  • @Lagerbaer 我想知道monkeypatching是否可以紧凑地实现,或者这意味着我应该从monkeypatch切换到mock.patch

标签: python pytest monkeypatching


【解决方案1】:

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

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

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

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

现在至于修补本身,这取决于您的个人喜好,并且有点取决于您究竟想要修补什么,是使用更紧凑的 unittest.mock.patch 还是 pytest 的 monkeypatch 固定装置。

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

现在请注意这种类型的测试:不要过火!它很容易导致所谓的脆弱测试:对代码进行最轻微更改的测试就会中断。它可以使重构成为不可能的噩梦。

当您在以消息为中心的面向对象方法中使用这些类型的断言时,它们是最好的。如果被测类或方法的整个 point 是以特定方式调用另一个对象的方法或类,则 Mock away。另一方面,如果对第三方函数的调用只是达到目的的一种手段,那么请通过您的测试更上一层楼并测试所需的行为

【讨论】:

  • 感谢您的详尽回答!我有一个问题,我可以在Mock()return_value arg 中添加一个函数吗?我实际上正在使用 Microsoft O365 python 库来访问云中的驱动器和文件。我必须创建自定义方法,因为并非所有逻辑都已实现。例如,我的方法创建了正确的文件夹目录,我必须调用库的 get_some_itemċreate_some_folder_here 类型的方法,其中包含正确的循环/检查。显然我无法在测试中访问真正的驱动器,所以我不得不模拟这些方法和对象。你会称之为行为测试吗?
  • return_value 可以是任何你喜欢的。如果不自己设置,它会自动成为一个新的 Mock 对象,以此类推。如果你设置它,你可以例如传入一些虚拟类或存根类。因此,在您的用例示例中,您使用 Mocks 来“排除”第三方依赖项,该依赖项太昂贵而无法测试。这当然是 Mocks 和修补的合适用例。
猜你喜欢
  • 2021-03-26
  • 2012-08-19
  • 2011-01-14
  • 1970-01-01
  • 2010-09-29
  • 2012-07-14
  • 2013-04-13
  • 2011-10-06
  • 2020-05-24
相关资源
最近更新 更多