python 检查是否调用了一个方法而不用 mock 掉它

python check if a method is called without mocking it away

class A():
    def tmp(self):
        print("hi")

def b(a):
    a.tmp()

查看b中是否调用了tmp方法,推荐的方式是

a = A()
a.tmp = MagicMock()
b(a)
a.tmp.assert_called()

但是这里的 tmp 被模拟掉了,不会导致 "hi" 被打印出来。

我希望我的单元测试检查方法 tmp 是否被调用而不用 mock 掉它。

这可能吗?

我知道这不是编写单元测试时期望的标准事情。但是我的用例(有点棘手)需要这个。

您可以将Mock.side_effect设置为原始方法。

from unittest.mock import MagicMock

class A():
    def tmp(self):
        print("hi")

def b(a):
    a.tmp()

a = A()
a.tmp = MagicMock(side_effect=a.tmp)
b(a)
a.tmp.assert_called()

side_effect是一个函数(或本例中的绑定方法,这是一种函数)时,调用Mock也会调用side_effect参数。

Mock() 调用将 return 无论 side_effect return 是什么,除非它 return 是 unnittest.mock.DEFAULT singleton. Then it will return Mock.return_value

或者你可以修饰方法来测试:

def check_called(fun):
    def wrapper(self, *args, **kw):
        attrname = "_{}_called".format(fun.__name__)
        setattr(self, attrname, True)
        return fun(self, *args, **kw)
    return wrapper


a = A()
a.tmp = check_called(a.tmp)
b(a)
assert(getattr(a, "_tmp_called", False))

但是如果您已经在使用 Mock,MagicMock side_effect 绝对是更好的解决方案 ;)

除了@Patrick Haugh 的 , you can also pass the function to the wraps 论点(对我来说它在语义上似乎更正确)。

wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).

a = A()
a.tmp = MagicMock(wraps=a.tmp)
b(a)
a.tmp.assert_called()