对于单元测试包装器库的情况,目标是在不依赖/执行上游库的情况下测试包装器;在一个已知的情况下,所有对上游库的调用都可以被模拟,这就是我所做的,但是我对包装器的更改感到沮丧,因为包装器的引入使模拟工具错过了对上游库的更多调用。

我怎样才能最好地通过尝试使用给定名称空间的任何测试?

我目前的想法是将所有单元测试方法更改为具有猴子补丁

@unittest.mock.patch('wrapper_namespace.upsteam_namespace')


并使用可以断言的模拟结果回复上游库;我希望有一个可以在全球范围内使用的选项,以便


尽管这种粒度级别是可以接受的,但不必在每个测试方法中都添加一个猴子补丁。但也不必执行断言,即测试方法中从未使用过该模拟(或使装饰器可以执行所有操作)
禁止从软件的任何部分访问上游库
(例如,包装器呼叫B呼叫上游,可能无法捕获B的上游呼叫)

最佳答案

您不必修补所有测试方法。如果您使用的是class,则可以轻松地修补unittest,或者只是将模块分配给要修补的模块。这是一个可行的示例:

some_lib.py中的伪造lib:

def some_lib_func():
    raise ValueError("I've been called.")

def some_other_lib_func():
    raise ValueError("I've been called.")

class SomeClass:
    def __init__(self):
        raise ValueError("I've been constructed.")


wrapper.py:

import some_lib

def wrapper1():
    some_lib.some_lib_func()

def wrapper2():
    some_lib.some_other_lib_func()

def wrapper3():
    x = some_lib.SomeClass()


test.py:

from unittest.mock import patch, MagicMock
import unittest

import wrapper

# Alternative:
# wrapper.some_lib = MagicMock()

# Can patch an entire class
@patch('wrapper.some_lib', MagicMock())
class TestWrapper(unittest.TestCase):
    def test_wrapper1(self):
        wrapper.wrapper1()

    def test_wrapper2(self):
        wrapper.wrapper2()

    def test_wrapper3(self):
        wrapper.wrapper3()

if __name__ == "__main__":
    unittest.main()


如果some_lib中的函数/类被调用,我们会爆炸,但它们不是:

Matthews-MacBook-Pro:stackoverflow matt$ python test.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK


随意注释掉补丁并在wrapper.some_lib = MagicMock()中注释。在此玩具示例中,您将获得相同的结果,但是两种方法之间存在主要差异:

使用@patch('wrapper.some_lib', MagicMock())时,该修补程序仅适用于该测试用例类。

但是,使用wrapper.some_lib = MagicMock()时,除非您保存了原始模块并在某个时候手动将其修补,否则该修补程序将在整个python程序中保持有效。使用wrapper模块的所有内容都会获得模拟版本。

所以你可以这样:

original_lib = wrapper.some_lib
wrapper.some_lib = MagicMock()
...
# call some test suite, every call to the wrapper module will be mocked out
...
wrapper.some_lib = original_lib
...
# call some other test suite that actually needs the real thing
...


HTH。

编辑:稍微误解了您的问题,但是您可以检查MagicMock对象以查看是否已调用它们,如果是,则测试失败。或者只是修补一些在调用时失败的东西(而不是MagicMock)。如果需要,我可以提供执行此操作的代码(只需发表评论),但希望以上内容可以帮助您入门。我认为问题的症结在于真正的全球修补。干杯!

10-06 12:48