2015-01-08 7 views
15

Der Titel beschrieb einfach mein Problem. Ich möchte "_func_inner_1" mit einem bestimmten Rückgabewert vortäuschen. Vielen Dank für jede berät :)Wie eine Tornado-Coroutine-Funktion mit Mock-Framework für Komponententests zu verspotten?

im Test befindlichen Code:

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) 

Aber diese intuitive Lösung

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) 

Mit unterhalb Fehler nicht funktioniert, scheint _func_inner_1 nicht gepatcht aufgrund seiner Koroutine Natur

AssertionError: 2 

wenn ich Coroutine zum Patch zurückgeben mock-Funktion

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

der Fehler wird:

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__ 

Dies ist die nächste Lösung, die ich finden kann, aber die spöttische Funktion wird nicht nach dem Testfall Ausführung zurückgesetzt werden, im Gegensatz zu welchem ​​Patch tut

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

Antwort

22

Es gibt zwei Probleme hier:

Zuerst ist die Interaktion zwischen @mock.patch a nd @gen_test. gen_test funktioniert, indem ein Generator in eine "normale" Funktion umgewandelt wird; mock.patch funktioniert nur bei normalen Funktionen (soweit der Dekorator dies feststellen kann, kehrt der Generator zurück, sobald er die erste yield erreicht, also macht mock.patch alle seine Arbeit rückgängig). Um dieses Problem zu vermeiden, können Sie entweder die Dekorateure neu anordnen (Put immer @mock.patchvor@gen_test, oder verwenden Sie die with Form von mock.patch anstelle der Dekorateur Form.

Zweitens Koroutinen sollte niemals eine Ausnahme ausgelöst. Stattdessen werden sie zurückkehren a Future, die ein Ergebnis oder eine Ausnahme enthalten wird.Die spezielle Exception Return wird vom Coroutinsystem eingekapselt, Sie würden es nie aus einer Zukunft herausheben.Wenn Sie Ihre Mocks erstellen, müssen Sie die entsprechende Future erstellen und als Rückgabewert festlegen anstatt side_effect zu verwenden, um eine Ausnahme auszulösen

Die komplette Lösung i s:

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() 
+1

es funktioniert perfekt, vielen dank :) –

Verwandte Themen