已经关注测试异步应用程序的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。
答案 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)