Tuning concurrency¶
--concurrency caps how many tests run at once; the default is 16. Whether that number is doing
anything for a given suite is a question --durations answers, not a guess:
The multiplier at the end is the sum of every test's own duration divided by the wall time the run
actually took. Above 1.0, tests overlapped; at or below 1.0, they didn't — either the suite is too
fast for scheduling to matter, or something is holding it to one test at a time regardless of
--concurrency.
Reading what --durations names¶
--durations N lists the N slowest tests at the end of the run — under concurrency, the slowest
test is the floor the wall clock can't drop below, so it's the list to read before touching
--concurrency at all. A test that patches a module or a class installs by mutating something
every other running test can see, so voci schedules it to run alone and drains the suite around
it:
@mock.patch("relay.client.random.uniform", return_value=1.0)
async def test_jitter_is_deterministic(
uniform: mock.MagicMock,
r: Relay = Depends(flaky_relay),
t: FakeTransport = Depends(flaky_transport),
) -> None:
"""`relay.client.random.uniform` is a module global, so this test is scheduled to run alone
and the suite drains around it — the summary line at the end of the run says how long that
took.
"""
...
A suite with several of these shows up as a low multiplier and a --durations list topped by
tests that, individually, aren't slow — they're just serialized against everything else. The fix
is rarely --concurrency; it's removing the reason a test needs to run alone. Here, that's an
injectable jitter: Callable[[], float] argument on Relay in place of patching
random.uniform — dependency injection runs fully concurrently, because nothing about it is a
process-global write:
async def test_transport_substituted_by_di(
r: Relay = Depends(flaky_relay),
t: FakeTransport = Depends(flaky_transport),
) -> None:
"""No patching at all. Runs alongside every other test in the suite."""
await r.deliver("https://hooks.test/v1", b"x")
assert len(t.sent) == 3
A run's footer breaks out solo time on its own line when any test ran that way:
Setting --concurrency¶
Once solo-scheduled tests are accounted for, --concurrency is what's left to tune. Raising it
helps a suite bottlenecked on waiting — I/O, sleeps, network calls — up to whatever the underlying
resource (a connection pool, a rate limit, the number of cores available to sync tests) can
actually sustain; past that point the multiplier stops climbing and only memory and contention go
up. --concurrency 1 (or --serial) is the floor, useful for isolating a single test's true cost
rather than tuning a whole suite — see Debugging a flaky test.
The full mocking ladder is in examples/02-async-library/tests/test_patching.py.