Elixir "mix test" Flaky Async Failures in CI
ExUnit tests marked async: true run concurrently. A test that passes alone but fails intermittently in CI usually shares state with another async test, assumes ordering, or hits the database sandbox in the wrong mode.
What this error means
A mix test run fails intermittently - different tests on different runs, often with assertion mismatches or "owner ... not allowed" sandbox errors. Re-running passes, and the failures cluster around async: true modules.
1) test creates record (App.WorkerTest)
Assertion failed, expected 1 record, got 2
# or
** (DBConnection.OwnershipError) cannot find ownership process for #PID<...>Common causes
Shared mutable state across async tests
Two async: true tests touch the same global/ETS/registry/external resource. Running concurrently they interleave, producing nondeterministic results.
Sandbox not in shared mode for spawned processes
When a test spawns processes that hit the DB, the Ecto SQL sandbox must allow them; without shared mode under concurrency you get ownership errors.
How to fix it
Isolate state or drop async for the conflicting tests
- Identify tests that share a global resource and set those modules to
async: false. - Replace shared global state with per-test setup (unique names, fresh ETS tables).
- Avoid asserting on ordering that concurrency does not guarantee.
Configure the SQL sandbox correctly
Use the sandbox in a mode that supports the processes your test spawns.
# test/support setup
:ok = Ecto.Adapters.SQL.Sandbox.checkout(App.Repo)
Ecto.Adapters.SQL.Sandbox.mode(App.Repo, {:shared, self()})How to prevent it
- Keep
async: truetests free of shared mutable state. - Use unique per-test identifiers instead of fixed global names.
- Configure the Ecto sandbox correctly for spawned processes.