【问题标题】:How can I autospec mock attributes that are None by default in python 3?如何在 python 3 中自动指定默认为 None 的模拟属性?
【发布时间】:2014-03-02 22:13:26
【问题描述】:

考虑这段代码:

import unittest
from unittest.mock import patch

class Foo(object):
    def __init__(self, bar=None):
        self.bar = bar

    def methodA(self):
        print("In methodA")

    def methodB(self):
        print("In methodB")


def my_func(bar):
    foo = Foo(bar)
    if foo.bar:
        foo.methodA()
    foo.methodB()


class MyTestCase(unittest.TestCase):
    def test_my_func(self):
        bar = None

        with patch("__main__.Foo", autospec=True) as foo:
            my_func(bar)
            foo.methodB.assert_called_once_with()


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

这个想法相当简单。我有一个函数,它的行为会根据实例属性的存在与否进行切换。我正在尝试编写一个单元测试来验证仅执行某些 Foo 方法,具体取决于属性。

基于模拟库的patchautospeccing 文档,我认为在patch() 上下文管理器中设置autospec=True 就足够了。它没有。生成的Mock() 正确包含了methodAmethodB 的模拟,但测试失败并出现以下错误:

======================================================================
ERROR: test_my_func (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "so.py", line 28, in test_my_func
    my_func(bar)
  File "trash.py", line 18, in my_func
    if foo.bar:
  File "/.../python3.3/unittest/mock.py", line 549, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'bar'

我确定我遗漏了一些明显的东西,但我似乎无法弄清楚是什么。如何对my_func() 进行单元测试?

【问题讨论】:

    标签: python unit-testing python-3.x mocking python-unittest


    【解决方案1】:

    我的部分问题是对补丁行为的误解。我设置的上下文管理器返回的是__main__.Foo 模拟的实例,而不是my_func() 中使用的相同实例。换句话说,即使我能够正确地模拟 Foo,但没有 autospec,我也无法在其任何方法上执行 assert_called_once_with():它不是同一个对象。

    一种解决方案是模拟方法本身。这有效:

    def test_my_func(self):
        bar = None
        with patch('__main__.Foo.methodB') as mock_methodB:
            my_func(bar)
            mock_methodB.assert_called_once_with()
    

    另一种方法是修改 my_func() 以返回 foo:

    def my_func(bar):
        foo = Foo(bar)
        if foo.bar:
            foo.methodA()
        foo.methodB()
        return foo
    

    因为该函数返回被测模拟,所以以下应该可以工作:

    def test_my_func(self):
        bar = None
        with patch('__main__.Foo', spec=True, bar=None):
            foo = my_func(bar)
            assert foo.methodB.called
            assert not foo.methodA.called
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-13
      • 1970-01-01
      • 2012-04-11
      • 1970-01-01
      • 1970-01-01
      • 2022-01-10
      • 2013-05-20
      • 1970-01-01
      相关资源
      最近更新 更多