The suggested testing pattern requires boilerplate duplication of app code and doesn't show testing the app directly.
https://docs.aiohttp.org/en/stable/testing.html#pytest
I should be able to pass my app module directly to test_client
to test my app.
The docs show building the app from handlers on the fly before each test. Passing the real app
directly to test_client
works fine the first time, then fails with "RuntimeError: web.Application instance initialized with different loop".
# 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'
I hacked around the issue for now by peeking at the source code and determining the cause of the error.
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
It is not immediately clear to my why this code is necessary. I could not find any context in the git history. Setting app._loop = None
in my test cleanup method seems to avoid the problem without any negative side effects. Is this code actually necessary? Could this fix be integrated into test_client
?
Application
is a stateful object, it should be recreated for every test from unit test suite.
The application's lifespan should be shorter than event loop's one.
Hint: replace app.app
with async def app.make_app()
coroutine which returns a new app on every call.
Add def make_app()
to app.py
? That makes sense.
FWIW tornado promotes the make_app()
pattern in their very first example. I don't think mocking the app in the test file is a good pattern for the docs to promote.
Honestly I don't think mocking any aiohttp
object is a good pattern
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a [new issue] for related bugs.
If you feel like there's important points made in this discussion, please include those exceprts into that [new issue].
Most helpful comment
Application
is a stateful object, it should be recreated for every test from unit test suite.The application's lifespan should be shorter than event loop's one.
Hint: replace
app.app
withasync def app.make_app()
coroutine which returns a new app on every call.