Насмешка над классом, использованным в операторе with - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть класс с функциями __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 (один или несколько раз), и (связанный) как мне установить результаты этих вызовов (используя .side_effect или .return_value)?

1 Ответ

0 голосов
/ 11 февраля 2019

Оператор with принимает все, что возвращается __enter__, для привязки к имени в части as <name>.Вы привязали его к 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() функция завершится с ошибкой атрибута.

...