tornado.testing
— 用于异步代码的单元测试支持¶
用于自动化测试的支持类。
AsyncTestCase
和AsyncHTTPTestCase
: unittest.TestCase 的子类,对测试异步 (IOLoop
基于) 代码提供了额外的支持。ExpectLog
: 使测试日志不那么冗长。main()
: 一个简单的测试运行器 (unittest.main() 的包装器),支持 tornado.autoreload 模块在代码更改时重新运行测试。
异步测试用例¶
- class tornado.testing.AsyncTestCase(methodName: str = 'runTest')[source]¶
TestCase
子类,用于测试IOLoop
基于的异步代码。unittest 框架是同步的,因此测试必须在测试方法返回之前完成。这意味着异步代码不能像通常那样使用,必须进行调整以适应。要使用协程编写测试,请用
tornado.testing.gen_test
而不是tornado.gen.coroutine
来装饰你的测试方法。此类还提供了 (已弃用) 的
stop()
和wait()
方法,用于更手动的方式进行测试。测试方法本身必须调用self.wait()
,而异步回调应该调用self.stop()
来表示完成。默认情况下,每个测试都会构建一个新的
IOLoop
,并且可以作为self.io_loop
使用。如果被测试的代码需要一个可重用的全局IOLoop
,子类应该覆盖get_new_ioloop
以返回它,尽管从 Tornado 6.3 开始,此方法已弃用。IOLoop
的start
和stop
方法不应该直接调用。相反,请使用self.stop
和self.wait
。传递给self.stop
的参数将从self.wait
返回。在同一个测试中可以有多个wait
/stop
循环。示例
# This test uses coroutine style. class MyTestCase(AsyncTestCase): @tornado.testing.gen_test def test_http_fetch(self): client = AsyncHTTPClient() response = yield client.fetch("https://tornado.pythonlang.cn") # 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("https://tornado.pythonlang.cn/", self.stop) response = self.wait() # Test contents of response self.assertIn("FriendFeed", response.body)
- get_new_ioloop() IOLoop [source]¶
返回用于此测试的
IOLoop
。默认情况下,每个测试都会创建一个新的
IOLoop
。子类可以覆盖此方法以返回IOLoop.current()
,如果在每个测试中使用新的IOLoop
不合适(例如,如果存在使用默认IOLoop
的全局单例),或者如果每个测试的事件循环是由另一个系统(例如pytest-asyncio
)提供的。已弃用,版本 6.3: 此方法将在 Tornado 7.0 中移除。
- wait(condition: Optional[Callable[[...], bool]] = None, timeout: Optional[float] = None) Any [source]¶
运行
IOLoop
直到调用 stop 或超时。超时情况下,将抛出异常。 默认超时时间为 5 秒,可以通过
timeout
关键字参数或全局环境变量ASYNC_TEST_TIMEOUT
覆盖。如果
condition
不为None
,则在调用stop()
后,IOLoop
将重新启动,直到condition()
返回True
。版本 3.1 中变更: 添加了环境变量
ASYNC_TEST_TIMEOUT
。
- class tornado.testing.AsyncHTTPTestCase(methodName: str = 'runTest')[source]¶
启动 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() Application [source]¶
应由子类覆盖,返回
tornado.web.Application
或其他HTTPServer
回调。
- fetch(path: str, raise_error: bool = False, **kwargs: Any) HTTPResponse [source]¶
同步获取 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 响应代码引发的错误。
- class tornado.testing.AsyncHTTPSTestCase(methodName: str = 'runTest')[source]¶
启动 HTTPS 服务器的测试用例。
接口与
AsyncHTTPTestCase
基本相同。
- tornado.testing.gen_test(*, timeout: Optional[float] = None) Callable[[Callable[[...], Union[Generator, Coroutine]]], Callable[[...], None]
- tornado.testing.gen_test(func: Callable[[...], Union[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())
代替。版本 3.1 中新增: 参数
timeout
和环境变量ASYNC_TEST_TIMEOUT
。版本 4.0 中更改: 包装器现在将传递
*args, **kwargs
,以便它可以用于带参数的函数。
控制日志输出¶
- class tornado.testing.ExpectLog(logger: Union[Logger, str], regex: str, required: bool = True, level: Optional[int] = None)[source]¶
上下文管理器用于捕获和抑制预期的日志输出。
有助于使错误条件测试更安静,同时仍然让意外日志条目可见。 *不线程安全*。
属性
logged_stack
设置为True
,如果任何异常堆栈跟踪被记录。用法
with ExpectLog('tornado.application', "Uncaught exception"): error_response = self.fetch("/some_page")
版本 4.3 中更改: 添加了
logged_stack
属性。构造 ExpectLog 上下文管理器。
- 参数
logger – 要观察的记录器对象(或记录器名称)。 传递空字符串以观察根记录器。
regex – 要匹配的正则表达式。 指定记录器上的任何匹配此正则表达式的日志条目都将被抑制。
required – 如果为 true,如果在达到
with
语句的末尾时没有匹配任何日志条目,则将引发异常。level – 来自
logging
模块的常量,指示预期的日志级别。 如果提供此参数,则只有此级别的日志消息将被认为匹配。 此外,提供的logger
将根据需要调整其级别(在ExpectLog
的持续时间内)以启用预期的消息。
版本 6.1 中更改: 添加了
level
参数。版本 6.3 中弃用: 在 Tornado 7.0 中,默认情况下只有
WARNING
和更高的日志级别将被匹配。 要匹配INFO
和更低的级别,必须使用level
参数。 正在更改此设置以最大程度地减少tornado.testing.main
(默认情况下启用INFO
日志)与大多数其他测试运行器(包括 IDE 中的测试运行器)之间的差异,这些测试运行器默认情况下禁用了INFO
日志。
测试运行器¶
- tornado.testing.main(**kwargs: Any) None [source]¶
一个简单的测试运行器。
这个测试运行器本质上等同于标准库中的
unittest.main
,但它添加了对 Tornado 风格选项解析和日志格式的支持。使用AsyncTestCase
运行测试时,没有必要使用这个main
函数;这些测试是自包含的,可以使用任何测试运行器运行。运行测试的最简单方法是通过命令行
python -m tornado.testing tornado.test.web_test
请参考标准库
unittest
模块,了解如何指定测试。具有大量测试的项目可能希望定义一个测试脚本,例如
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)
在运行时显示许多测试详细信息。有关完整参数列表,请参考 https://docs.pythonlang.cn/library/unittest.html#unittest.main。版本 5.0 中的变化: 此函数不会自行生成任何输出;只生成由
unittest
模块生成的输出(以前它会添加一个 PASS 或 FAIL 日志消息)。