用于龙卷风测试异步应用的ExceptionStackContext

时间:2015-04-21 08:00:27

标签: tornado

已经关注测试异步应用程序的ajdavis @ pycon2015,如下所示: http://pyvideo.org/video/3419/eventually-correct-testing-async-apps

我对ExceptionStackContext的使用有疑问。在演示文稿中,它使用如下:

import unittest
from tornado.stack_context import ExceptionStackContext
from tornado.ioloop import IOLoop

class TestAsync(unittest.TestCase):
    def test_delay(self):
        io_loop = IOLoop.current()
        def handle_exception(typ, val, tb):
            self.failure = (typ, val, tb)
            io_loop.stop()
        def done():
            self.assertAlmostEqual(time.time() - start, 2)
            self.stop()
        with ExceptionStackContext(handle_exception):
            delay(3, done)  # fail the assert
        io_loop.start()

运行此测试,io_loop不会停止。因为没有调用handle_exception。

我的异步方法delay是:

import threading
import time

def delay(delay_seconds, callback):
    def wrap():    
        time.sleep(delay_seconds)
        callback()
    t = threading.Thread(target=wrap)
    t.daemon = True
    t.start()

所以我认为,ExceptionStackContext应该包装done(),如下所示:

def callback():
    with ExceptionStackContext(handle_exception):
        done()
delay(2, callback)
io_loop.start()

这是使用ExceptionStackContext的正确方法吗?

顺便说一句,tornado.testing.AsyncTestCase中的ExceptionStackContext实际上是无用的:

def run(self, result=None):
    with ExceptionStackContext(self._handle_exception):
        super(AsyncTestCase, self).run(result)

super(AsyncTestCase, self).run(result)不会抛出AssertException。

2 个答案:

答案 0 :(得分:1)

StackContexts是神奇的:Tornado中有很多地方可以自动捕获当前StackContext并稍后恢复。因此,当delay()调用done()时(假设delay()IOLoop.add_timeout实现或以其他方式正确处理StackContext),ExceptionStackContext已重新建立

同样,即使ExceptionStackContext中的AsyncTestCase.run()永远不会直接捕获任何异常,它也会在测试中的任何位置建立“当前”StackContext。 (这就是为什么它是with ExceptionStackContext而不是普通的try/except

答案 1 :(得分:0)

如果你仔细地按照例子,那么ExceptionStackContext就像我声称的那样工作。 It's much easier to follow if you read my article

你使用unittest.TestCase调用了“self.stop”,但是TestCase没有self.stop,只有AsyncTestCase。要使用TestCase,请尝试:

import unittest
import time
from tornado.stack_context import ExceptionStackContext
from tornado.ioloop import IOLoop


def delay(seconds, callback):
    io_loop = IOLoop.current()
    io_loop.add_timeout(io_loop.time() + seconds, callback)


class TestAsync(unittest.TestCase):
    def test_delay(self):
        io_loop = IOLoop.current()
        start = time.time()

        def handle_exception(typ, val, tb):
            self.failure = (typ, val, tb)
            io_loop.stop()

        def done():
            self.assertAlmostEqual(time.time() - start, 2, places=1)
            io_loop.stop()

        with ExceptionStackContext(handle_exception):
            delay(3, done)  # fail the assert
        io_loop.start()

注意我必须声明“start”,你不在你的代码中,并设置“places = 1”。这工作正常。或者使用AsyncTestCase:

import time

from tornado import testing
from tornado.ioloop import IOLoop


def delay(seconds, callback):
    io_loop = IOLoop.current()
    io_loop.add_timeout(io_loop.time() + seconds, callback)


class TestAsync(testing.AsyncTestCase):
    def test_delay(self):
        start = time.time()
        delay(3, self.stop)
        self.wait()
        self.assertAlmostEqual(time.time() - start, 2, places=1)