Как насмехаться с функцией торнадо coroutine с использованием макетной рамки для модульного тестирования?
Название просто описывает мою проблему. Я хотел бы высмеять "_func_inner_1" с конкретным возвращаемым значением. Спасибо за любые советы:)
проверенный код:
from tornado.gen import coroutine, Return
from tornado.testing import gen_test
from tornado.testing import AsyncTestCase
import mock
@coroutine
def _func_inner_1():
raise Return(1)
@coroutine
def _func_under_test_1():
temp = yield _func_inner_1()
raise Return(temp + 1)
Но это интуитивное решение не работает
class Test123(AsyncTestCase):
@gen_test
@mock.patch(__name__ + '._func_inner_1')
def test_1(self, mock_func_inner_1):
mock_func_inner_1.side_effect = Return(9)
result_1 = yield _func_inner_1()
print 'result_1', result_1
result = yield _func_under_test_1()
self.assertEqual(10, result, result)
С ошибкой ниже, кажется, что _func_inner_1 не исправлен из-за этого coroutine nature
AssertionError: 2
если я добавляю coroutine для исправления возвращенной функции mock
@gen_test
@mock.patch(__name__ + '._func_inner_1')
def test_1(self, mock_func_inner_1):
mock_func_inner_1.side_effect = Return(9)
mock_func_inner_1 = coroutine(mock_func_inner_1)
result_1 = yield _func_inner_1()
print 'result_1', result_1
result = yield _func_under_test_1()
self.assertEqual(10, result, result)
ошибка становится:
Traceback (most recent call last):
File "tornado/testing.py", line 118, in __call__
result = self.orig_method(*args, **kwargs)
File "tornado/testing.py", line 494, in post_coroutine
timeout=timeout)
File "tornado/ioloop.py", line 418, in run_sync
return future_cell[0].result()
File "tornado/concurrent.py", line 109, in result
raise_exc_info(self._exc_info)
File "tornado/gen.py", line 175, in wrapper
yielded = next(result)
File "coroutine_unit_test.py", line 39, in test_1
mock_func_inner_1 = coroutine(mock_func_inner_1)
File "tornado/gen.py", line 140, in coroutine
return _make_coroutine_wrapper(func, replace_callback=True)
File "tornado/gen.py", line 150, in _make_coroutine_wrapper
@functools.wraps(func)
File "functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
File "mock.py", line 660, in __getattr__
raise AttributeError(name)
AttributeError: __name__
Это самое близкое решение, которое я могу найти, но функция mocking не будет reset после выполнения тестового примера, в отличие от того, что патч
@gen_test
def test_4(self):
global _func_inner_1
mock_func_inner_1 = mock.create_autospec(_func_inner_1)
mock_func_inner_1.side_effect = Return(100)
mock_func_inner_1 = coroutine(mock_func_inner_1)
_func_inner_1 = mock_func_inner_1
result = yield _func_under_test_1()
self.assertEqual(101, result, result)
Ответы
Ответ 1
Здесь есть две проблемы:
Сначала это взаимодействие между @mock.patch
и @gen_test
. gen_test работает путем преобразования генератора в "нормальную" функцию; mock.patch работает только на обычных функциях (насколько может сказать декоратор, генератор возвращается, как только он достигает первого yield
, поэтому mock.patch отменяет всю свою работу). Чтобы избежать этой проблемы, вы можете либо изменить порядок декораторов (всегда ставьте @mock.patch
до @gen_test
), либо используйте форму with
mock.patch
вместо формы декоратора.
Во-вторых, сопрограммы никогда не должны создавать исключение. Вместо этого они возвращают Future
, который будет содержать результат или исключение. Специальное исключение Return
инкапсулировано системой сопрограммы; вы никогда не поднимете его из Будущего. Когда вы создаете свои mocks, вы должны создать соответствующее Будущее и установить его как возвращаемое значение вместо использования side_effect для повышения на исключение.
Полное решение:
from tornado.concurrent import Future
from tornado.gen import coroutine, Return
from tornado.testing import gen_test
from tornado.testing import AsyncTestCase
import mock
@coroutine
def _func_inner_1():
raise Return(1)
@coroutine
def _func_under_test_1():
temp = yield _func_inner_1()
raise Return(temp + 1)
class Test123(AsyncTestCase):
@mock.patch(__name__ + '._func_inner_1')
@gen_test
def test_1(self, mock_func_inner_1):
future_1 = Future()
future_1.set_result(9)
mock_func_inner_1.return_value = future_1
result_1 = yield _func_inner_1()
print 'result_1', result_1
result = yield _func_under_test_1()
self.assertEqual(10, result, result)
import unittest
unittest.main()