Pythonでasyncioを使うアプリケーションのテストを書くとき、event loopに気を遣ってあげないとエラーになるという話です (今回はaiohttpを使ってWebアプリを作っているときに起こりました)。
こんなエラーがでました。
RuntimeError: Task <Task pending coro=<...> cb=[...]> got Future <Future pending> attached to a different loop
テスト側で動かすイベントループと、アプリ側が動かそうとする、イベントループに違いがあると発生してしまいます。 例えばアプリ側で何らかのバックエンドにアクセスするクライアントを使っていて、テスト側でもデータ(フィクスチャー)を作ったりクリーンアップしたいとします。 そのときに双方の使うクライアントが内部で使っているイベントループが違うものだと上記のエラーが発生します。
テスト用の pytest-asyncio
などのライブラリーが、テスト用のイベントループを用意する場合は、アプリ側でクライアントを起動時にモジュールグローバルに保存しないようにしておきます。
そのクライアントがインスタンス化されるときに、内部で asyncio.get_event_loop
を呼ぶことがあるからです。
もしくはテスト時にクライアントを差し替えるようにします。
この例だと pytest-asyncio
の event_loop
フィクスチャーを取っておくことでテスト用のイベントループを有効にしておきます。
import pytest import somebackend @pytest.fixture async def somebackend(event_loop): client = somebackend.Client() try: yield client finally: do_cleanup_here()
テストでは以下のように使えます。
@pytest.mark.asyncio async def test_foo(somebackend): await somebackend.create("some thing") actual = await call_function_under_test() assert actual == "expected"
今回は aiodocker
を使っていて起こりました。
この情報が何かの参考になれば嬉しいです。 また、asyncioやイベントループ周りについて詳しければ教えてください。
近そうな話