Как да се подигравам на сърутинна функция торнадо с помощта на макетна рамка за тестване на единици?

Заглавието просто описва моя проблем. Бих искал да се подиграя на "_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 не е коригиран поради съпрограмата си

AssertionError: 2

ако добавя съпрограма към кръпка, върна макетна функция

@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__

Това е най-близкото решение, което мога да намеря, но подигравателната функция НЯМА да бъде нулирана след изпълнение на тестов случай, за разлика от това, което кръпката прави

@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) 

person Jim Horng    schedule 08.01.2015    source източник


Отговори (1)


Тук има два проблема:

Първо е взаимодействието между @mock.patch и @gen_test. gen_test работи, като преобразува генератор в "нормална" функция; mock.patch работи само с нормални функции (доколкото декораторът може да разбере, генераторът се връща веднага щом достигне първия yield, така че mock.patch отменя цялата си работа). За да избегнете този проблем, можете или да пренаредите декораторите (винаги поставяйте @mock.patch преди @gen_test, или да използвате формата with на mock.patch вместо формата на декоратора.

Второ, съпрограммите никога не трябва да предизвикват изключение. Вместо това те връщат Future, което ще съдържа резултат или изключение. Специалното изключение Return е капсулирано от сърутинната система; никога не бихте го вдигнали от Бъдеще. Когато създавате своите подигравки, трябва да създадете подходящия Future и да го зададете като върната стойност, вместо да използвате 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()
person Ben Darnell    schedule 10.01.2015