Tornado 对异步代码的单元测试支持
AsyncTestCase
和 AsyncHTTPTestCase
:unittest.TestCase 的子类,额外支持测试异步(基于 IOLoop
)代码。
ExpectLog
:减少测试日志的垃圾邮件。
main()
:一个简单的测试运行器(围绕 unittest.main()
的包装),支持 tornado.autoreload
模块,以便在代码更改时重新运行测试。
异步测试用例
class tornado.testing.AsyncTestCase(methodName: str = 'runTest')
TestCase
子类,用于测试基于 IOLoop
的异步代码。
unittest
框架是同步的,所以测试必须在测试方法返回时完成。 这意味着异步代码不能像往常一样以完全相同的方式使用,必须进行调整以适应。 要使用协程编写测试,请使用 tornado.testing.gen_test
而不是 tornado.gen.coroutine
装饰您的测试方法。
此类还提供了(已弃用的)stop()
和 wait()
方法,用于更手动的测试风格。 测试方法本身必须调用 self.wait()
,异步回调应该调用 self.stop()
来表示完成。
默认情况下,会为每个测试构建一个新的 IOLoop
,并以 self.io_loop
的形式提供。 如果正在测试的代码需要全局 IOLoop
,子类应该覆盖 get_new_ioloop
以返回它。
不应直接调用 IOLoop
的启动和停止方法。 相反,使用 self.stop
和 self.wait
。 传递给 self.stop
的参数从 self.wait
返回。 在同一个测试中可以有多个等待或者停止周期。
例如:
# This test uses coroutine style.
class MyTestCase(AsyncTestCase):
@tornado.testing.gen_test
def test_http_fetch(self):
client = AsyncHTTPClient()
response = yield client.fetch("http://www.tornadoweb.org")
# Test contents of response
self.assertIn("FriendFeed", response.body)
# This test uses argument passing between self.stop and self.wait.
class MyTestCase2(AsyncTestCase):
def test_http_fetch(self):
client = AsyncHTTPClient()
client.fetch("http://www.tornadoweb.org/", self.stop)
response = self.wait()
# Test contents of response
self.assertIn("FriendFeed", response.body)
get_new_ioloop() → tornado.ioloop.IOLoop
返回要用于此测试的 IOLoop
。
默认情况下,为每个测试创建一个新的 IOLoop
。 如果不适合在每个测试中使用新的 IOLoop
(例如,如果有使用默认 IOLoop
的全局单例)或者如果正在提供每个测试的事件循环,则子类可以覆盖此方法以返回 IOLoop.current()
由另一个系统(例如 pytest-asyncio
)。
stop(_arg: Any = None, **kwargs) → None
停止 IOLoop
,导致对 wait()
的一个挂起(或者future
)调用返回。
传递给 stop()
的关键字参数或单个位置参数被保存并由 wait()
返回。
5.1 版后已弃用:stop
和wait
已弃用; 改用@gen_test
。
wait(condition: Optional[Callable[[...], bool]] = None, timeout: Optional[float] = None) → Any
运行 IOLoop
直到调用 stop
或timeout
。
如果timeout
,将抛出异常。 默认timeout
为 5 秒; 它可以被timeout
关键字参数或全局的 ASYNC_TEST_TIMEOUT
环境变量覆盖。
如果 condition
不是 None
,则 IOLoop
将在 stop()
之后重新启动,直到 condition()
返回 True
。
在 3.1 版更改: 添加了 ASYNC_TEST_TIMEOUT
环境变量。
5.1 版后已弃用:停止和等待已弃用; 改用@gen_test
。
class tornado.testing.AsyncHTTPTestCase(methodName: str = 'runTest')
启动 HTTP 服务器的测试用例。
子类必须覆盖 get_app()
,它返回要测试的 tornado.web.Application
(或其他 HTTPServer
回调)。 测试通常会使用提供的 self.http_client
从该服务器获取 URL。
示例,假设用户指南中的“Hello, world”示例在 hello.py 中:
import hello
class TestHelloApp(AsyncHTTPTestCase):
def get_app(self):
return hello.make_app()
def test_homepage(self):
response = self.fetch('/')
self.assertEqual(response.code, 200)
self.assertEqual(response.body, 'Hello, world')
对 self.fetch()
的调用等效于
self.http_client.fetch(self.get_url('/'), self.stop)
response = self.wait()
这说明了 AsyncTestCase
如何将异步操作(如 http_client.fetch()
)转换为同步操作。 如果您需要在测试中执行其他异步操作,您可能需要自己使用 stop()
和 wait()
。
get_app() → tornado.web.Application
应该被子类覆盖以返回 tornado.web.Application
或其他 HTTPServer
回调。
fetch(path: str, raise_error: bool = False, **kwargs) → tornado.httpclient.HTTPResponse
同步获取 URL 的便捷方法。
给定的路径将附加到本地服务器的主机和端口。任何其他关键字参数都将直接传递给 AsyncHTTPClient.fetch
(因此可用于传递 method="POST"
、body="..."
等)。
如果路径以 http://
或 https://
开头,则会将其视为完整 URL 并按原样获取。
如果 raise_error
为 True
,则如果响应代码不是 200,则会引发 tornado.httpclient.HTTPError
。这与 AsyncHTTPClient.fetch
的 raise_error
参数的行为相同,但此处默认为 False
(在 AsyncHTTPClient
中为 True
),因为测试经常需要处理非 200 响应码。
在 5.0 版更改: 添加了对绝对 URL 的支持。
在 5.1 版更改: 添加 raise_error
参数。
5.1 版后已弃用:此方法当前将任何异常转换为状态码为 599 的 HTTPResponse
。在 Tornado 6.0 中,将传递除 tornado.httpclient.HTTPError
以外的错误,并且 raise_error=False
只会抑制由于以下原因引发的错误非 200 响应代码
get_httpserver_options() → Dict[str, Any]
可以被子类覆盖以返回服务器的附加关键字参数。
get_http_port() → int
返回服务器使用的端口。
为每个测试选择一个新端口。
get_url(path: str) → str
返回测试服务器上给定路径的绝对 url。
class tornado.testing.AsyncHTTPSTestCase(methodName: str = 'runTest')
启动 HTTPS 服务器的测试用例。
接口一般与 AsyncHTTPTestCase
相同。
get_ssl_options() → Dict[str, Any]
可以被子类覆盖以选择 SSL 选项。
默认情况下包括自签名测试证书。
tornado.testing.gen_test(func: Optional[Callable[[...], Union[collections.abc.Generator, Coroutine]]] = None, timeout: Optional[float] = None) → Union[Callable[[...], None], Callable[[Callable[[...], Union[collections.abc.Generator, Coroutine]]], Callable[[...], None]]]
测试等效于@gen.coroutine
,应用于测试方法。
@gen.coroutine
不能用于测试,因为 IOLoop
尚未运行。 @gen_test
应该应用于 AsyncTestCase
子类的测试方法。
例如:
class MyTest(AsyncHTTPTestCase):
@gen_test
def test_something(self):
response = yield self.http_client.fetch(self.get_url('/'))
默认情况下,@gen_test
在 5 秒后超时。 可以使用 ASYNC_TEST_TIMEOUT
环境变量全局覆盖超时,或者使用 timeout
关键字参数对每个测试进行覆盖:
class MyTest(AsyncHTTPTestCase):
@gen_test(timeout=10)
def test_something_slow(self):
response = yield self.http_client.fetch(self.get_url('/'))
请注意,@gen_test
与 AsyncTestCase.stop
、AsyncTestCase.wait
和 AsyncHTTPTestCase.fetch
不兼容。 如上所示,使用 yield self.http_client.fetch(self.get_url())
代替。
控制日志输出
class tornado.testing.ExpectLog(logger: Union[logging.Logger, str], regex: str, required: bool = True, level: Optional[int] = None)
上下文管理器,用于捕获和抑制预期的日志输出。
有助于减少错误条件测试,同时仍使意外的日志条目可见。 这并不是线程安全的。
如果记录了任何异常堆栈跟踪,则属性 logged_stack
设置为 True
。
例如:
with ExpectLog('tornado.application', "Uncaught exception"):
error_response = self.fetch("/some_page")
在 4.3 版更改: 添加了 logged_stack
属性。
构造一个 ExpectLog
上下文管理器。
参数:
-
logger
-- 要观察的 Logger 对象(或 logger 的名称)。 传递一个空字符串来观察根记录器。 -
regex
-- 要匹配的正则表达式。 指定记录器上与此正则表达式匹配的任何日志条目都将被禁止。 -
required
- 如果为 true,如果到达 with 语句的末尾但没有匹配任何日志条目,则会引发异常。 -
level
- 来自日志记录模块的常量,指示预期的日志级别。 如果提供此参数,则仅此级别的日志消息将被视为匹配。 此外,提供的记录器将在必要时调整其级别(在 ExpectLog
的持续时间内启用预期的消息)。
在 6.1 版更改: 添加了 level
参数。
测试
tornado.testing.main(**kwargs) → None
一个简单的测试运行器。
这个测试运行器本质上等同于标准库中的 unittest.main
,但增加了对 Tornado 样式选项解析和日志格式的支持。 使用 AsyncTestCase
运行测试不需要使用这个 main 函数; 这些测试是独立的,可以与任何测试运行器一起运行。
运行测试的最简单方法是通过命令行:
python -m tornado.testing tornado.test.web_test
具有许多测试的项目可能希望定义一个测试脚本,例如 tornado/test/runtests.py。 这个脚本应该定义一个方法 all()
,它返回一个测试套件,然后调用 tornado.testing.main()
。 请注意,即使使用了测试脚本,也可以通过在命令行上命名单个测试来覆盖 all()
测试套件:
# Runs all tests
python -m tornado.test.runtests
# Runs one test
python -m tornado.test.runtests tornado.test.web_test
传递给 unittest.main()
的其他关键字参数。 例如,使用 tornado.testing.main(verbosity=2)
在运行时显示许多测试详细信息。
在 5.0 版更改: 此函数不产生自己的输出; 仅由 unittest
模块生成(以前它会添加 PASS
或 FAIL
日志消息)。
辅助函数
tornado.testing.bind_unused_port(reuse_port: bool = False) → Tuple[socket.socket, int]
将服务器socket绑定到 localhost 上的可用端口。
返回一个元组(socket,port)。
在 4.4 版更改: 始终绑定到 127.0.0.1 而不解析名称 localhost。
tornado.testing.get_async_test_timeout() → float
获取异步测试的全局超时设置。
返回一个浮点数,以秒为单位的超时。