question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Question] Async test client

See original GitHub issue

I’m developing an app that requires async setup and teardown during testing. For example, I insert data into a Redis database using aioredis. I’m using pytest-asyncio to mark async test functions.

The test client uses the synchronous requests API and an ASGI adapter to call the Starlette app. The setup I described above doesn’t work with the adapter because it doesn’t expect an event loop to be running.

I haven’t worked with asyncio prior to using Starlette so I may be missing something obvious but do you have any suggestions for how to deal with this? I thought about more explicitly managing the event loop during testing but this seemed a bit tedious.

Another option was an async test client. I had a look at aiohttp and if a custom Connector could be used in place of the ASGI requests adapter but I didn’t spend much time on it.

For now, I cobbled together an async test client that combines the request setup from requests.Session and starlette.testclient. _ASGIAdapter.

By the way, if there’s a better place to ask questions, please let me know 😄

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

10reactions
and-semakincommented, Nov 17, 2020

I’ll just put links here for future strangers.

Also awesome explanation about the async test client: https://fastapi.tiangolo.com/advanced/async-tests/ (it’s about FastAPI but it still should be applicable to pure Starlette).

My example of async test client fixture:

# conftest.py
from asgi_lifespan import LifespanManager
from httpx import AsyncClient


@pytest.fixture()
async def client() -> Iterator[AsyncClient]:
    async with LifespanManager(app):
        async with AsyncClient(app=app, base_url="http://test") as ac:
            yield ac


# test_app.py
@pytest.mark.asyncio
async def test_endpoint(client: AsyncClient) -> None:
    response = await client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Tomato"}
5reactions
ryankaskcommented, Nov 9, 2018

@mackeyja92 Here’s a gist: https://gist.github.com/ryankask/90c0dc155d3f95a52a1eaa0ac4ef63d0.

I needed to make it async all the way down to this call loop.run_until_complete(connection(receive, send)) deep in the adapter so a nested loop wasn’t started.

To do this, I combined the ASGI adapter Tom wrote with requests’ Session API. I removed some of the Session-related code that I didn’t think was relevant but I may have removed or left in too much.

I use pytest with pytest-asyncio doing something like this


@pytest.fixture
def client():
    return StarletteTestClient(starlette_app)

@pytest.mark.asyncio
async def test_update_company(client):
    company = await CompanyStore.create(name="Great Software")
    data = {
        "name": "Awesome Software",
    }

    response = await client.patch(f"/companies/{company.id}", json=data)

    assert response.status_code == 200
    updated_company = await CompanyStore.get(company.id)
    assert updated_company.name == "Awesome Software"

All my app code is async so to test in same way I’ve always done (arrange, act, assert, this time asynchronously), I think I need an async test client. This way pytest-asyncio can manage the loop and I can use pytest naturally. Maybe I need to just change the way I test so hopefully someone points out a better way.

I thought it would be better to use aiohttp’s equivalent of a requests’ adapter but I didn’t spend much time figuring it out.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Async Tests - FastAPI
The TestClient does some magic inside to call the asynchronous FastAPI application in your normal def test functions, using standard pytest. But that...
Read more >
async test patterns for Pytest - Anthony Shaw
The problem then comes at testing time. I've found async testing in both pytest and unittest painful, confusing and buggy.
Read more >
Can we use Flask test_client() inside async function
In short, flask can handle async views, and flask's test_client can be used in an async context, but you cannot use both at...
Read more >
Async Programming - Unit Testing Asynchronous Code
This illustrates the first lesson from the async/await conceptual model: To test an asynchronous method's behavior, you must observe the task it returns....
Read more >
Async test client for TCP socket - Code Review Stack Exchange
I've made some modifications to the async client example found on MSDN for the purpose of testing a server application.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found