龙卷风测试异步应用程序的 ExceptionStackContext
ExceptionStackContext for tornado testing async app
看过 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。
如果您仔细按照示例进行操作,那么 ExceptionStackContext 会像我声称的那样工作。 It's much easier to follow if you read my article.
您用 unittest.TestCase 调用了 "self.stop",但 TestCase 没有 self.stop,只有 AsyncTestCase 有。要使用测试用例,请尝试:
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)
StackContexts
很神奇:Tornado 中有很多地方可以自动捕获当前的 StackContext
并稍后恢复。因此,当 delay()
调用 done()
时(假设 delay()
是根据 IOLoop.add_timeout
实现的,或者以其他方式正确处理 StackContext
),ExceptionStackContext
已重新建立.
类似地,即使 AsyncTestCase.run()
中的 ExceptionStackContext
从未直接捕获任何异常,它建立了 "current" StackContext
以在测试中的任何地方捕获。 (这就是为什么它是 with ExceptionStackContext
而不是普通的 try/except
)
看过 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。
如果您仔细按照示例进行操作,那么 ExceptionStackContext 会像我声称的那样工作。 It's much easier to follow if you read my article.
您用 unittest.TestCase 调用了 "self.stop",但 TestCase 没有 self.stop,只有 AsyncTestCase 有。要使用测试用例,请尝试:
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)
StackContexts
很神奇:Tornado 中有很多地方可以自动捕获当前的 StackContext
并稍后恢复。因此,当 delay()
调用 done()
时(假设 delay()
是根据 IOLoop.add_timeout
实现的,或者以其他方式正确处理 StackContext
),ExceptionStackContext
已重新建立.
类似地,即使 AsyncTestCase.run()
中的 ExceptionStackContext
从未直接捕获任何异常,它建立了 "current" StackContext
以在测试中的任何地方捕获。 (这就是为什么它是 with ExceptionStackContext
而不是普通的 try/except
)