Tornado 對(duì)異步代碼的單元測(cè)試支持

2022-03-11 14:16 更新

?AsyncTestCase ?和 ?AsyncHTTPTestCase?:unittest.TestCase 的子類,額外支持測(cè)試異步(基于 ?IOLoop?)代碼。

?ExpectLog?:減少測(cè)試日志的垃圾郵件。

?main()?:一個(gè)簡(jiǎn)單的測(cè)試運(yùn)行器(圍繞 ?unittest.main()? 的包裝),支持 ?tornado.autoreload? 模塊,以便在代碼更改時(shí)重新運(yùn)行測(cè)試。

異步測(cè)試用例

class tornado.testing.AsyncTestCase(methodName: str = 'runTest')

?TestCase子類,用于測(cè)試基于 ?IOLoop的異步代碼。

?unittest框架是同步的,所以測(cè)試必須在測(cè)試方法返回時(shí)完成。 這意味著異步代碼不能像往常一樣以完全相同的方式使用,必須進(jìn)行調(diào)整以適應(yīng)。 要使用協(xié)程編寫測(cè)試,請(qǐng)使用 ?tornado.testing.gen_test? 而不是 ?tornado.gen.coroutine? 裝飾您的測(cè)試方法。

此類還提供了(已棄用的)?stop()? 和 ?wait()? 方法,用于更手動(dòng)的測(cè)試風(fēng)格。 測(cè)試方法本身必須調(diào)用 ?self.wait()?,異步回調(diào)應(yīng)該調(diào)用 ?self.stop()? 來(lái)表示完成。

默認(rèn)情況下,會(huì)為每個(gè)測(cè)試構(gòu)建一個(gè)新的 ?IOLoop?,并以 ?self.io_loop? 的形式提供。 如果正在測(cè)試的代碼需要全局 ?IOLoop?,子類應(yīng)該覆蓋 ?get_new_ioloop? 以返回它。

不應(yīng)直接調(diào)用 ?IOLoop? 的啟動(dòng)和停止方法。 相反,使用 ?self.stop? 和 ?self.wait?。 傳遞給 ?self.stop? 的參數(shù)從 ?self.wait? 返回。 在同一個(gè)測(cè)試中可以有多個(gè)等待或者停止周期。

例如:

# 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

返回要用于此測(cè)試的 ?IOLoop?。

默認(rèn)情況下,為每個(gè)測(cè)試創(chuàng)建一個(gè)新的 ?IOLoop?。 如果不適合在每個(gè)測(cè)試中使用新的 ?IOLoop?(例如,如果有使用默認(rèn) ?IOLoop的全局單例)或者如果正在提供每個(gè)測(cè)試的事件循環(huán),則子類可以覆蓋此方法以返回 ?IOLoop.current()? 由另一個(gè)系統(tǒng)(例如 ?pytest-asyncio?)。

stop(_arg: Any = None, **kwargs) → None

停止 ?IOLoop?,導(dǎo)致對(duì) ?wait()? 的一個(gè)掛起(或者?future?)調(diào)用返回。

傳遞給 ?stop()? 的關(guān)鍵字參數(shù)或單個(gè)位置參數(shù)被保存并由 ?wait()? 返回。

5.1 版后已棄用:?stop?和?wait?已棄用; 改用?@gen_test?。

wait(condition: Optional[Callable[[...], bool]] = None, timeout: Optional[float] = None) → Any

運(yùn)行 ?IOLoop直到調(diào)用 ?stop或?timeout?。

如果?timeout?,將拋出異常。 默認(rèn)?timeout?為 5 秒; 它可以被?timeout?關(guān)鍵字參數(shù)或全局的 ?ASYNC_TEST_TIMEOUT? 環(huán)境變量覆蓋。

如果 ?condition不是 ?None,則 ?IOLoop將在 ?stop()? 之后重新啟動(dòng),直到 ?condition()? 返回 ?True?。

在 3.1 版更改: 添加了 ?ASYNC_TEST_TIMEOUT? 環(huán)境變量。

5.1 版后已棄用:停止和等待已棄用; 改用?@gen_test?。

class tornado.testing.AsyncHTTPTestCase(methodName: str = 'runTest')

啟動(dòng) HTTP 服務(wù)器的測(cè)試用例。

子類必須覆蓋 ?get_app()?,它返回要測(cè)試的 ?tornado.web.Application?(或其他 ?HTTPServer回調(diào))。 測(cè)試通常會(huì)使用提供的 ?self.http_client? 從該服務(wù)器獲取 URL。

示例,假設(shè)用戶指南中的“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')

對(duì) ?self.fetch()? 的調(diào)用等效于

self.http_client.fetch(self.get_url('/'), self.stop)
response = self.wait()

這說(shuō)明了 ?AsyncTestCase如何將異步操作(如 ?http_client.fetch()?)轉(zhuǎn)換為同步操作。 如果您需要在測(cè)試中執(zhí)行其他異步操作,您可能需要自己使用 ?stop()? 和 ?wait()?。

get_app() → tornado.web.Application

應(yīng)該被子類覆蓋以返回 ?tornado.web.Application? 或其他 ?HTTPServer回調(diào)。

fetch(path: str, raise_error: bool = False, **kwargs) → tornado.httpclient.HTTPResponse

同步獲取 URL 的便捷方法。

給定的路徑將附加到本地服務(wù)器的主機(jī)和端口。任何其他關(guān)鍵字參數(shù)都將直接傳遞給 ?AsyncHTTPClient.fetch?(因此可用于傳遞 ?method="POST"?、?body="..."? 等)。

如果路徑以 ?http://? 或 ?https://? 開(kāi)頭,則會(huì)將其視為完整 URL 并按原樣獲取。

如果 ?raise_error? 為 ?True,則如果響應(yīng)代碼不是 200,則會(huì)引發(fā) ?tornado.httpclient.HTTPError?。這與 ?AsyncHTTPClient.fetch? 的 ?raise_error? 參數(shù)的行為相同,但此處默認(rèn)為 ?False?(在 ?AsyncHTTPClient中為 ?True),因?yàn)闇y(cè)試經(jīng)常需要處理非 200 響應(yīng)碼。

在 5.0 版更改: 添加了對(duì)絕對(duì) URL 的支持。

在 5.1 版更改: 添加 ?raise_error參數(shù)。

5.1 版后已棄用:此方法當(dāng)前將任何異常轉(zhuǎn)換為狀態(tài)碼為 599 的 ?HTTPResponse。在 Tornado 6.0 中,將傳遞除 ?tornado.httpclient.HTTPError? 以外的錯(cuò)誤,并且 ?raise_error=False? 只會(huì)抑制由于以下原因引發(fā)的錯(cuò)誤非 200 響應(yīng)代碼

get_httpserver_options() → Dict[str, Any]

可以被子類覆蓋以返回服務(wù)器的附加關(guān)鍵字參數(shù)。

get_http_port() → int

返回服務(wù)器使用的端口。

為每個(gè)測(cè)試選擇一個(gè)新端口。

get_url(path: str) → str

返回測(cè)試服務(wù)器上給定路徑的絕對(duì) url。

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

啟動(dòng) HTTPS 服務(wù)器的測(cè)試用例。

接口一般與 ?AsyncHTTPTestCase? 相同。

get_ssl_options() → Dict[str, Any]

可以被子類覆蓋以選擇 SSL 選項(xiàng)。

默認(rèn)情況下包括自簽名測(cè)試證書。

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]]]

測(cè)試等效于?@gen.coroutine?,應(yīng)用于測(cè)試方法。

?@gen.coroutine? 不能用于測(cè)試,因?yàn)?nbsp;?IOLoop尚未運(yùn)行。 ?@gen_test? 應(yīng)該應(yīng)用于 ?AsyncTestCase? 子類的測(cè)試方法。

例如:

class MyTest(AsyncHTTPTestCase):
    @gen_test
    def test_something(self):
        response = yield self.http_client.fetch(self.get_url('/'))

默認(rèn)情況下,?@gen_test? 在 5 秒后超時(shí)。 可以使用 ?ASYNC_TEST_TIMEOUT環(huán)境變量全局覆蓋超時(shí),或者使用 ?timeout關(guān)鍵字參數(shù)對(duì)每個(gè)測(cè)試進(jìn)行覆蓋:

class MyTest(AsyncHTTPTestCase):
    @gen_test(timeout=10)
    def test_something_slow(self):
        response = yield self.http_client.fetch(self.get_url('/'))

請(qǐng)注意,?@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)

上下文管理器,用于捕獲和抑制預(yù)期的日志輸出。

有助于減少錯(cuò)誤條件測(cè)試,同時(shí)仍使意外的日志條目可見(jiàn)。 這并不是線程安全的。

如果記錄了任何異常堆棧跟蹤,則屬性 ?logged_stack設(shè)置為 ?True?。

例如:

with ExpectLog('tornado.application', "Uncaught exception"):
    error_response = self.fetch("/some_page")

在 4.3 版更改: 添加了 ?logged_stack屬性。

構(gòu)造一個(gè) ?ExpectLog上下文管理器。

參數(shù):

  • ?logger-- 要觀察的 Logger 對(duì)象(或 logger 的名稱)。 傳遞一個(gè)空字符串來(lái)觀察根記錄器。
  • ?regex-- 要匹配的正則表達(dá)式。 指定記錄器上與此正則表達(dá)式匹配的任何日志條目都將被禁止。
  • ?required- 如果為 true,如果到達(dá) with 語(yǔ)句的末尾但沒(méi)有匹配任何日志條目,則會(huì)引發(fā)異常。
  • ?level- 來(lái)自日志記錄模塊的常量,指示預(yù)期的日志級(jí)別。 如果提供此參數(shù),則僅此級(jí)別的日志消息將被視為匹配。 此外,提供的記錄器將在必要時(shí)調(diào)整其級(jí)別(在 ?ExpectLog的持續(xù)時(shí)間內(nèi)啟用預(yù)期的消息)。

在 6.1 版更改: 添加了 ?level參數(shù)。

測(cè)試

tornado.testing.main(**kwargs) → None

一個(gè)簡(jiǎn)單的測(cè)試運(yùn)行器。

這個(gè)測(cè)試運(yùn)行器本質(zhì)上等同于標(biāo)準(zhǔn)庫(kù)中的 ?unittest.main?,但增加了對(duì) Tornado 樣式選項(xiàng)解析和日志格式的支持。 使用 ?AsyncTestCase運(yùn)行測(cè)試不需要使用這個(gè) main 函數(shù); 這些測(cè)試是獨(dú)立的,可以與任何測(cè)試運(yùn)行器一起運(yùn)行。

運(yùn)行測(cè)試的最簡(jiǎn)單方法是通過(guò)命令行:

python -m tornado.testing tornado.test.web_test

具有許多測(cè)試的項(xiàng)目可能希望定義一個(gè)測(cè)試腳本,例如 tornado/test/runtests.py。 這個(gè)腳本應(yīng)該定義一個(gè)方法 ?all()?,它返回一個(gè)測(cè)試套件,然后調(diào)用 ?tornado.testing.main()?。 請(qǐng)注意,即使使用了測(cè)試腳本,也可以通過(guò)在命令行上命名單個(gè)測(cè)試來(lái)覆蓋 ?all()? 測(cè)試套件:

# Runs all tests
python -m tornado.test.runtests
# Runs one test
python -m tornado.test.runtests tornado.test.web_test

傳遞給 ?unittest.main()? 的其他關(guān)鍵字參數(shù)。 例如,使用 ?tornado.testing.main(verbosity=2)? 在運(yùn)行時(shí)顯示許多測(cè)試詳細(xì)信息。

在 5.0 版更改: 此函數(shù)不產(chǎn)生自己的輸出; 僅由 ?unittest模塊生成(以前它會(huì)添加 ?PASS或 ?FAIL日志消息)。

輔助函數(shù)

tornado.testing.bind_unused_port(reuse_port: bool = False) → Tuple[socket.socket, int]

將服務(wù)器socket綁定到 localhost 上的可用端口。

返回一個(gè)元組(socket,port)。

在 4.4 版更改: 始終綁定到 127.0.0.1 而不解析名稱 localhost。

tornado.testing.get_async_test_timeout() → float

獲取異步測(cè)試的全局超時(shí)設(shè)置。

返回一個(gè)浮點(diǎn)數(shù),以秒為單位的超時(shí)。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)