Использование @inlineCallbacks в тестовых модульных тестах Twisted
У меня есть несколько пробных юнит-тестов. В моей кодовой базе некоторые методы возвращают deferreds с добавленными обратными вызовами, в то время как другие украшены @inlineCallbacks. Я хочу запустить тест без реактора, так как некоторые методы не выполняют никаких операций ввода-вывода. Я подумал, что @inlineCallbacks просто возвращает отложенный метод, поэтому достаточно вызвать callbacks(0) для его запуска. Кажется, не тот случай. Вот минимальный пример:
from twisted.trial import unittest
from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
def addone(val):
d = Deferred()
def cbk(res):
return val + 1
d.addCallback(cbk)
return d
@inlineCallbacks
def call_addone(val):
res = yield addone(val)
returnValue(res)
class Tester(unittest.TestCase):
def test_addone(self):
d = addone(2)
d.callback(0) # whatever I pass is ignored
self.assertEqual(3, self.successResultOf(d))
def test_call_addone(self):
d = call_addone(4)
d.callback(0) # whatever I pass is set as deferred's result
self.assertEqual(5, self.successResultOf(d))
Когда я вызываю addone(2), я возвращаю deferred, который возвращает 2+1, когда я запускаю его с callback(0). В этом случае значение, переданное обратному вызову, игнорируется. Во втором тесте call_addone(4) также возвращает deferred. Но в этом случае передаваемый ему параметр игнорируется, вместо этого возвращаемое значение будет тем, что я передаю в callback(). Зачем? Я явно что-то упускаю.
Вот вывод пробного бегуна:
test_trial
Tester
test_addone ... [OK]
test_call_addone ... [FAIL]
===============================================================================
[FAIL]
Traceback (most recent call last):
File "tests/test_trial.py", line 31, in test_call_addone
self.assertEqual(5, self.successResultOf(d))
File "/home/b/.local/share/virtualenvs/twproba-y019OThE/lib/python3.5/site-packages/twisted/trial/_synctest.py", line 432, in assertEqual
super(_Assertions, self).assertEqual(first, second, msg)
File "/usr/lib/python3.5/unittest/case.py", line 821, in assertEqual
assertion_func(first, second, msg=msg)
File "/usr/lib/python3.5/unittest/case.py", line 814, in _baseAssertEqual
raise self.failureException(msg)
twisted.trial.unittest.FailTest: 5 != 0
test_trial.Tester.test_call_addone
-------------------------------------------------------------------------------
Ran 2 tests in 0.029s
FAILED (failures=1, successes=1)
1 ответ
Эти строки в значительной степени неверны (учитывая, что call_addone
определяется с inlineCallbacks
):
d = call_addone(4)
d.callback(0) # whatever I pass is set as deferred's result
Это согласуется с тем, что эти строки странные (учитывая, что inlineCallbacks
не используется):
d = addone(2)
d.callback(0) # whatever I pass is ignored
В обоих случаях вы запрашиваете некоторый библиотечный код для создания Deferred
а затем код вашего приложения предоставляет результат для Deferred
, поскольку addone
делает то, что вы хотите, ясно, что можно использовать Deferred
сюда. Тем не менее, это не очень хорошая практика. Лучшая практика заключается в ответственности за создание и увольнение Deferred
лежать на том же месте. Таким образом, в этом случае, с реализацией addone
,
Причиной неудачного теста является то, что вы не учли реализацию inlineCallbacks
который считает, что он действительно несет ответственность за увольнение Deferred
это возвращается. И Deferred
это возвращается не Deferred
вернулся addone
звонить это делает. Это держит это Deferred
к себе.
Результат d
в:
d = call_addone(4)
d.callback(0) # whatever I pass is set as deferred's result
является 0
именно потому, что вы предоставили результат 0. Вы передали результат inlineCallbacks
-удалось Deferred
и замкнуло все остальное осуществление inlineCallbacks
, Если Deferred
вернулся addone
когда-нибудь стрелял, вы, скорее всего, увидите AlreadyCalledError
так как inlineCallbacks
будет пытаться предоставить результат d
, Deferred
Вы уже предоставили результат.