Python3 mock:assert_has_calls用于生产代码方法?

时间:2016-04-02 17:06:56

标签: python python-3.x mocking

我有这个生产课程:

class MyClass:
    def __init__(self):
        self.value = None

    def set_value(self, value):
        self.value = value

    def foo(self):
        # work with self.value here
        # raise RuntimeError("error!")
        return "a"

其他地方使用的是这样的:

class Caller:
    def bar(self, smth):
        obj = MyClass()
        obj.set_value(smth)
        # ...
        # try:
        obj.foo()
        # except MyError:
        #     pass

        obj.set_value("str2")
        # obj.foo()

我得到了这个:

class MyError(Exception):
    pass

在我的测试中,我想确保Caller.bar调用obj.set_value,首先使用smth =" a"然后使用smth =" b",但我想要它真正设置值(即调用真正的set_value方法)。有没有办法让我告诉模拟使用实际的方法,所以我以后可以阅读它的名称?

P.S。我知道我可以改变" foo"要求参数" smth"所以我可以摆脱" set_value",但我想知道是否还有其他选择。

好的,所以我在测试中尝试了这个:

def test_caller(self):
     with patch('fullpath.to.MyClass', autospec=MyClass) as mock:
         mock.foo.side_effect = [MyError("msg"), "text"]

         caller = Caller()
         caller.bar("str1")

         calls = [call("str1"), call("str2")]
         mock.set_value.assert_has_calls(calls)

但是我看到模拟不成功,因为真正的" foo"当我希望它首先引发MyError,然后返回" text"。

时调用

此外,断言失败:

AssertionError: Calls not found.
Expected: [call('str1'), call('str2')]
Actual: []

1 个答案:

答案 0 :(得分:1)

这里的问题是您已经嘲笑了Class,并且没有正确使用您班级的实例。这就是事情不符合预期的原因。

所以,让我们来看看发生了什么。

就在这里:

with patch('fullpath.to.MyClass', autospec=MyClass) as mock:

所以,你在这里做的只是嘲笑你的班级MyClass。所以,当你这样做时:

mock.set_value.assert_has_calls(calls)

检查执行单元测试时发生的情况,mock来电实际上会包含此内容:

[call().set_value('str1'), call().foo(), call().set_value('str2')]

请注意call,因为它写为call()call参考了您的{​​{1}}。因此,考虑到这一点,您需要使用名为(在模拟世界的上下文中称为mockreturn_value来正确引用您尝试的模拟对象测试。解决此问题的快速方法是使用mock。所以你只需要改为:

mock()

但是,为了更清楚地了解您正在做什么,您可以声明您实际上正在使用调用mock().set_value.assert_has_calls(calls) 的结果。此外,除了mock之外,使用更明确的名称实际上是值得注意的。试试mock,然后将实例命名为MyClassMock

my_class_mock_obj

因此,在您的单元测试中,您更明确地使用了类的模拟对象。此外,最好在进行方法调用之前设置所有模拟,并且my_class_mock_obj = MyClassMock.return_value 确保您还使用实例模拟对象。根据您最近的异常处理更新,请保留您的try / except而不发表评论。把这一切放在一起,你有:

foo.side_effect