How to run tests asynchronously using pytest and gevent or another asynchronous approach?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Asynchronous testing in Python can mean two different things: testing code that is itself asynchronous, or trying to run multiple tests concurrently. Those are not the same problem, and pytest treats them differently. If your application uses gevent, asyncio, or another cooperative concurrency model, the clean approach is usually to test that code in its native style rather than trying to force the entire test suite to execute in parallel.
Decide What "Asynchronous Testing" Means
Most confusion comes from mixing these goals:
- write tests for asynchronous application code
- run many tests concurrently to speed up the suite
- simulate concurrent clients in one test
pytest is excellent at structuring tests, but it does not automatically make tests asynchronous or concurrent. You need the right library or plugin for the concurrency model your code uses.
Testing gevent-Based Code
If the application uses gevent, the main thing is to let greenlets run and then wait for them deterministically inside the test. You do not usually need a special pytest async syntax for that.
Here is a simple example:
This test is still a normal pytest test function. The asynchronous behavior is handled by gevent itself.
If your code depends on monkey patching, apply it very early in the process, ideally before importing networking libraries that should be patched.
That should usually live in test bootstrap code, not inside random test bodies.
Simulating Concurrent Work in One Test
A frequent requirement is not "run tests concurrently" but "exercise concurrent behavior within a single test". That works well with gevent.spawn and explicit joins.
That gives you deterministic coverage of concurrency logic without trying to execute unrelated test files in parallel.
For asyncio, Use the Right Plugin
If the code under test is based on asyncio, gevent is not the natural tool. Use pytest-asyncio and mark async tests explicitly.
This is usually the cleanest approach for modern Python async code. Do not mix gevent and asyncio in the same article mentally as if they were the same runtime model. They solve similar problems with different mechanics.
Running Tests Concurrently Is a Separate Concern
If your real goal is to speed up the suite, look at pytest-xdist for parallel execution across processes. That is not asynchronous testing in the application-code sense.
This runs tests in parallel workers, but it introduces its own constraints around shared state, database fixtures, ports, and temporary files.
Use this when the suite is embarrassingly parallel, not as a fix for async application logic.
Keep Tests Deterministic
Asynchronous systems become hard to test when timing is implicit. A few habits help:
- wait explicitly for all scheduled work to finish
- avoid arbitrary
sleepcalls when event or state checks are possible - isolate shared state between tests
- use timeouts around greenlet joins when hangs are possible
For example:
That prevents a broken test from hanging the suite indefinitely.
Common Pitfalls
A common mistake is trying to make pytest itself "asynchronous" when the real requirement is just to test gevent or asyncio code correctly.
Another mistake is confusing concurrent test execution with testing concurrent behavior inside one test. Those are different concerns.
People also often use arbitrary sleeps as synchronization. That creates flaky tests instead of deterministic ones.
Finally, avoid mixing runtime models casually. If the app uses asyncio, prefer pytest-asyncio. If it uses gevent, test around gevent primitives directly.
Summary
- Asynchronous testing can mean testing async code or running tests in parallel, and those are different goals
- For
gevent, ordinarypytesttests plus greenlets and explicit joins are often enough - For
asyncio, usepytest-asyncioandasync deftests - For faster suites, use process-level parallelism such as
pytest-xdist, not runtime async tools - Keep async tests deterministic with explicit waits and timeouts
- Choose the testing approach that matches the concurrency model your application actually uses

