【问题标题】:Mocking a class used in a with statement模拟 with 语句中使用的类
【发布时间】:2019-02-11 16:22:18
【问题描述】:

我有一个具有__exit____enter__ 函数的类,以便我可以在 with 语句中使用它,例如:

with ClassName() as c:
    c.do_something()

我现在正在尝试编写一个单元测试来测试它。基本上,我试图测试do_something() 只被调用过一次。

一个例子(我称之为testmocking1):

class temp:
    def __init__(self):
        pass

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def test_method(self):
        return 1


def fun():
    with temp() as t:
        return t.test_method()

我的测试:

import unittest
import test_mocking1
from test_mocking1 import fun
import mock
from mock import patch

class MyTestCase(unittest.TestCase):
    @patch('test_mocking1.temp', autospec = True)
    def test_fun_enter_called_once(self, mocked_object):
        fun()
        mocked_object.test_method.assert_called_once()

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

所以我希望这会通过,因为 test_method 在函数fun() 中只被调用了一次。但我得到的实际结果是:

======================================================================
FAIL: test_fun_enter_called_once (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<path_to_virtual_env>\lib\site-packages\mock\mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "<File_with_test>", line 11, in test_fun_enter_called_once
mocked_object.test_method.assert_called_once()
  File "<path_to_virtual_env>\lib\site- 
packages\mock\mock.py", line 915, in assert_called_once
    raise AssertionError(msg)
AssertionError: Expected 'test_method' to have been called once. Called 0 times.

如何测试使用with 语句创建的类中的函数是否已被调用(一次或多次),以及(相关)如何设置这些调用的结果(使用@987654331 @ 或 .return_value)?

【问题讨论】:

    标签: python python-2.7 mocking python-unittest


    【解决方案1】:

    with 语句将任何__enter__ 返回的内容绑定到as &lt;name&gt; 部分中的名称。你绑定到t:

    with temp() as t:
        t.test_method()
    

    注意temp() 被调用,所以with 语句以temp.return_value 开头。 t 也不是 temp.return_value,它是 temp().__enter__() 返回的任何内容,因此您需要使用该调用的返回值:

    entered = mocked_object.return_value.__enter__.return_value
    entered.test_method.assert_called_once()
    

    扩展至此,如果您想更改 test_method() 返回的内容,请更改 mocked_object.return_value.__enter__.return_value 的返回值。

    您可以随时打印出您的对象的mock_calls() attribute 以查看它发生了什么:

    >>> from test_mocking1 import fun
    >>> from mock import patch
    >>> with patch('test_mocking1.temp', autospec = True) as mocked_object:
    ...     fun()
    ...
    >>> print(mocked_object.mock_calls)
    [call(),
     call().__enter__(),
     call().__enter__().test_method(),
     call().__exit__(None, None, None)]
    >>> mocked_object.return_value.__enter__.return_value.test_method.called
    True
    >>> mocked_object.return_value.__enter__.return_value.test_method.call_count
    1
    

    请注意,temp.__enter__() 的实际实现会返回 None,因此如果不模拟您的 fun() 函数,则会因属性错误而失败。

    【讨论】:

    • 已编辑,以便我有一个实际的返回值以供将来参考。
    • entered = mocked_object.return_value.__enter__.return_value 很好的解决方案!
    猜你喜欢
    • 2011-02-26
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多