【问题标题】:Using @inlineCallbacks in Twisted trial unit tests在 Twisted 试用单元测试中使用 @inlineCallbacks
【发布时间】:2018-06-15 05:04:10
【问题描述】:

我有一些试用单元测试。在我的代码库中,一些方法返回延迟并添加回调,而其他方法则用@inlineCallbacks 装饰。我想在没有反应器的情况下运行测试,因为某些方法不执行任何 I/O。我坚持@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,当我使用 callback(0) 触发它时将返回 2+1。在这种情况下,传递给回调的值将被忽略。 在第二个测试 call_addone(4) 也返回一个 deferred。但在这种情况下,传递给它的参数将被忽略,而返回值将是我传递给回调()的任何值。为什么?我显然错过了一些东西。

这是试用运行器的输出:

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)

【问题讨论】:

    标签: python twisted deferred trial


    【解决方案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 不是它调用的addone 返回的Deferred。它将Deferred 保留给自己。

    d 中的结果:

        d = call_addone(4)
        d.callback(0)   # whatever I pass is set as deferred's result
    

    0 正是因为您提供的结果为0。您已将结果提供给inlineCallbacks-托管Deferred 并将inlineCallbacks 的所有其余实现短路。如果addone 返回的Deferred 确实触发了,您可能会看到AlreadyCalledError,因为inlineCallbacks 会尝试向d 提供结果,而Deferred 您已经向其提供了结果。

    【讨论】:

    • 简而言之:在我返回一个 new 延迟之前,它应该被解雇。如果我从其他地方(可能是 adbapi 或 inlineCallback)获得延迟,则无需触发延迟?
    • 你可以在它被触发之前返回它——事实上,这就是 Deferred 的主要用途。但是触发它的代码在逻辑上应该是与创建它的代码相同的“单元”的一部分。这是一个模糊的想法。我不知道适用于所有情况的硬性规则来告诉您是否正确的代码正在执行触发...嗯,一个很好的规则是,如果 API 文档没有告诉您 你必须解雇 Deferred,你不应该解雇它。但这在大多数情况下并没有多大帮助。
    猜你喜欢
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-25
    • 1970-01-01
    相关资源
    最近更新 更多