模拟在with语句中使用的类

11
我有一个类,该类具有__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()

我期望这个测试通过,因为fun()函数中确切地调用了一次test_method。但我实际得到的结果是:

======================================================================
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语句创建的类中的函数是否被调用(一次或多次),以及(相关的)如何设置这些调用的结果(使用.side_effect.return_value)?

1个回答

12
< p > with语句接受< code >__enter__返回的任何内容来绑定到< code >as <name>部分中的名称。您将其绑定到< code >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()属性以查看已发生的情况:

>>> 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()函数将因属性错误而失败。

已编辑,以便将来有实际的返回值参考。 - Diemo
entered = mocked_object.return_value.__enter__.return_value太好了,解决得很棒! - Zhong Ri

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