tornado.testing — 用于异步代码的单元测试支持

用于自动化测试的支持类。

  • AsyncTestCaseAsyncHTTPTestCase: 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 开始,此方法已弃用。

IOLoopstartstop 方法不应该直接调用。相反,请使用 self.stopself.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 中移除。

stop(_arg: Optional[Any] = None, **kwargs: Any) None[source]

停止 IOLoop,导致对 wait() 的一个挂起(或将来)调用返回。

传递给 stop() 的关键字参数或单个位置参数将被保存,并将由 wait() 返回。

已弃用,版本 5.1: stopwait 已弃用;请使用 @gen_test 代替。

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

已弃用,版本 5.1: stopwait 已弃用;请使用 @gen_test 代替。

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_errorTrue,如果响应代码不是 200,则会引发 tornado.httpclient.HTTPError。 这与 AsyncHTTPClient.fetchraise_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][source]

可以被子类覆盖,返回服务器的额外关键字参数。

get_http_port() int[source]

返回服务器使用的端口。

每个测试都会选择一个新的端口。

get_url(path: str) str[source]

返回测试服务器上给定路径的绝对 URL。

class tornado.testing.AsyncHTTPSTestCase(methodName: str = 'runTest')[source]

启动 HTTPS 服务器的测试用例。

接口与 AsyncHTTPTestCase 基本相同。

get_ssl_options() Dict[str, Any][source]

可以被子类覆盖来选择 SSL 选项。

默认情况下包含自签名的测试证书。

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_testAsyncTestCase.stopAsyncTestCase.waitAsyncHTTPTestCase.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 日志消息)。

辅助函数

tornado.testing.bind_unused_port(reuse_port: bool = False, address: str = '127.0.0.1') Tuple[socket, int][source]

将服务器套接字绑定到 localhost 上的可用端口。

返回一个元组 (socket, port)。

版本 4.4 中的变化: 始终绑定到 127.0.0.1,而不解析名称 localhost

版本 6.2 中的变化: 添加了可选的 address 参数来覆盖默认的“127.0.0.1”。

tornado.testing.get_async_test_timeout() float[source]

获取异步测试的全局超时设置。

返回一个浮点数,以秒为单位的超时时间。

版本 3.1 中的新增功能。