在Python中模拟assert_called_with

6
我有些困难理解以下代码为什么不能通过: test.py
import mock
import unittest

from foo import Foo


class TestFoo(unittest.TestCase):
    @mock.patch('foo.Bar')
    def test_foo_add(self, Bar):
        foo = Foo()
        foo.add(2, 2)
        Bar.add.assert_called_with(2, 2)


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

foo.py

from bar import Bar

class Foo(object):
    def add(self, x, y):
        bar = Bar()
        return bar.add(x, y)

bar.py

class Bar(object):
    def add(self, x, y):
        print('b.Bar --> Adding {} + {}'.format(x, y))
        return x + y

在代码中,Foo.add 创建了一个 Bar 的实例,并在调用时返回 Bar.add 的结果。为什么对于 Bar.add 进行的 assert_called_with 测试失败?我认为我在正确的位置模拟了 Bar(我正在模拟 foo.Bar,因为它是被查找的命名空间,而不是 bar.Bar)。
跟踪(Traceback)(最近的调用最先显示):
File "/Users/iain/PycharmProjects/testing/venv/lib/python2.7/site-packages/mock.py",第1201行,在修补程序(patched)中 return func(*args, **keywargs) File "test.py",第12行,在测试 a_b fake_Bar.add.assert_called_with(2, 2) File "/Users/iain/PycharmProjects/testing/venv/lib/python2.7/site-packages/mock.py",第831行,在 assert_called_with 中 raise AssertionError('Expected call: %s\nNot called' % (expected,)) 断言错误(AssertionError):期望的调用:add(2, 2)未被调用
2个回答

13

您正在适当地嘲笑方法调用。然而,由于您是从实例中调用方法,因此它是一个绑定方法,并因此接收实例作为第一个参数(self参数),以及所有其他参数。

编辑:由于Bar被替换为Mock实例,Bar().add不知道它是一个方法(因此没有绑定到任何内容)。换句话说,Bar是一个MockBar()是一个MockBar().add也是一个Mockbar.add因此是新创建的mock,使用参数(2, 2)进行调用。断言这个调用的一种方法是:

@mock.patch('foo.Bar')
def test_foo_add(self, Bar):
    foo = Foo()
    foo.add(2, 2)
    Bar.return_value.add.assert_called_with(2, 2)

根据你的实际代码情况,你可能更希望模拟方法而不是类:

@mock.patch('foo.Bar.add')
def test_foo_add(self, bar_add):
    foo = Foo()
    foo.add(2, 2)
    bar_add.assert_called_with(2, 2)

我已经修改了测试,以便将 foo 作为第一个参数传递给 Bar.add.assert_called_with(),但测试仍然失败了?输出结果相同(AssertionError: Expected call: add(<foo.Foo object at 0x102108a50>, 2, 2) Not called) - Iain
糟糕,我的示例代码完全错误。稍后会更新正确的答案。抱歉! - Felipe
谢谢!我认为我犯了一个错误,认为如果我嘲笑 foo.Bar 那么它的所有属性和方法都会保持原样,除非我明确地改变它们的行为。这种情况下是否应该使用 mock.patch.object 方法呢? - Iain
patch.objectpatch具有相同的功能;它们只是使用不同的引用方式来修补对象。要在foo模块中修补Bar,您可以使用mock.patch(foo, 'Bar'),结果将是相同的。不幸的是,我不知道有没有预先构建的方法可以获得您想要的行为。 - Felipe

-2

我相信你的问题可以这样解决:

from bar import Bar

class Foo(object):
    def add(self, x, y):
        self.bar = Bar()
        return self.bar.add(x, y)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接