Использование @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 Вы уже предоставили результат.

Другие вопросы по тегам