推奨されるテストパターンでは、アプリコードの定型的な複製が必要であり、アプリを直接テストすることは示されていません。
https://docs.aiohttp.org/en/stable/testing.html#pytest
アプリをテストするために、アプリモジュールをtest_client
に直接渡すことができるはずです。
ドキュメントには、各テストの前に、ハンドラーからその場でアプリを構築する方法が示されています。 実際のapp
をtest_client
app
直接渡すことは、最初は正常に機能し、その後「RuntimeError:web.Applicationインスタンスが異なるループで初期化されました」で失敗します。
# handler.py
async def foo():
return 'foo'
# app.py
from aiohttp import web
from .handler import foo
app = web.Application()
app.add_routes([web.get('/foo/', foo)])
# test.py
from .app import app
async def test_foo_status(test_client):
response = await client.get('/foo/')
assert response.status == 200
async def test_foo_body(test_client):
response = await client.get('/foo/')
body = await response.text()
assert body == 'foo'
今のところ、ソースコードを調べてエラーの原因を特定することで、この問題をハッキングしました。
if self._loop is not None and self._loop is not loop:
raise RuntimeError(
"web.Application instance initialized with different loop")
https://github.com/aio-libs/aiohttp/blob/e561eaa/aiohttp/web_app.py#L134
このコードが必要な理由はすぐにはわかりません。 gitの履歴にコンテキストが見つかりませんでした。 テストクリーンアップ方法でapp._loop = None
を設定すると、悪影響を与えることなく問題を回避できるようです。 このコードは実際に必要ですか? この修正をtest_client
統合できますか?
Application
はステートフルオブジェクトであり、ユニットテストスイートからのテストごとに再作成する必要があります。
アプリケーションの寿命は、イベントループの寿命よりも短くする必要があります。
ヒント: app.app
をasync def app.make_app()
コルーチンに置き換えて、呼び出しごとに新しいアプリを返します。
def make_app()
をapp.py
追加しますか? それは理にかなっている。
FWIWトルネードは、最初の例でmake_app()
パターンを促進します。 テストファイルでアプリをモックすることは、ドキュメントが宣伝するのに適したパターンではないと思います。
正直なところ、 aiohttp
オブジェクトをモックするのは良いパターンではないと思います
このスレッドは、閉じられた後、最近のアクティビティがないため、自動的にロックされています。 関連するバグについては、[新しい問題]を開いてください。
この議論で重要なポイントがあると思われる場合は、それらの抜粋をその[新刊]に含めてください。
最も参考になるコメント
Application
はステートフルオブジェクトであり、ユニットテストスイートからのテストごとに再作成する必要があります。アプリケーションの寿命は、イベントループの寿命よりも短くする必要があります。
ヒント:
app.app
をasync def app.make_app()
コルーチンに置き換えて、呼び出しごとに新しいアプリを返します。