Test quarantine
Test quarantine is moving a known-flaky test out of the build gate while it keeps running and reporting. The merge stops depending on it; the test does not stop producing evidence.
It exists because the two obvious responses to a flaky test are both bad. Leave it in the gate and the team learns to re-run red builds without reading them. Delete or skip it and the coverage — and the evidence needed to fix it — disappears.
Quarantine vs skip vs retry
| Still runs | Still reported | Can fail the build | |
|---|---|---|---|
| Normal test | Yes | Yes | Yes |
| Quarantined | Yes | Yes | No |
Skipped (skip, fixme) |
No | As skipped | No |
| Retried until green | Yes | Usually hidden | Only if every try fails |
Retries are the trap. They keep the test in the gate but throw away the signal, and most setups keep only the passing attempt's artifacts.
How to set it up
The pattern is the same everywhere: tag, exclude from the gate, run the tag in a job that is allowed to fail.
Playwright — tags are built in:
test("checkout applies coupon", { tag: "@quarantine" }, async ({ page }) => {
/* … */
});
npx playwright test --grep-invert @quarantine # gating job
npx playwright test --grep @quarantine # non-blocking job
pytest — register a marker so typos fail loudly:
# pytest.ini
[pytest]
markers = quarantine: known flaky, does not gate merges
pytest -m "not quarantine" # gating job
pytest -m quarantine # non-blocking job
Cypress — tag the test and filter with a grep plugin such as
@cypress/grep, using an inverted tag in the gating run.
In CI, the non-blocking job is continue-on-error: true on GitHub
Actions or allow_failure: true on GitLab — it still shows red, it just
does not block.
The rules that keep it honest
Quarantine without rules becomes a second suite that nobody reads. The ones that work:
- An owner and a ticket for every quarantined test, at the moment it is quarantined.
- A review date. Past it, the test is fixed, deleted, or explicitly re-quarantined by someone who looked.
- A size cap. When the quarantine set is full, nothing new goes in until something comes out.
- Exit on evidence. A test leaves quarantine after a run of consecutive passes, not after someone thinks it is fixed.
The evidence is the point
Quarantine only works if somebody reads the quarantined job's output. That output — the trace, the failure screenshot, the timings across runs — lives in the runner's HTML report, and in most pipelines that report is a build artifact that expires before the review date comes round.
Publish the quarantine job's report to the same link on every run and each build becomes a revision: the owner opens one URL and sees the latest failure, with earlier runs still in the history. Notes like "fails only on the 4-core runner" go on the failing test itself, where the next person to pick it up will see them (comment on HTML).
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.