Skip to content
Guide

How to detect and quarantine flaky tests automatically

Upload your test results to Mergify, let it classify each test from its own history, then turn on auto-quarantine so flaky tests stop failing your pipeline while they keep running and reporting. No test code changes, no manual list to maintain.

By Julien Danjou, Co-founder & CEO of MergifyUpdated

In one paragraph

Add one upload step to your CI job so every JUnit report reaches Mergify, including the runs that fail. After a few days, Detection lists the tests that pass and fail on the same commit and ranks them by how much CI time they waste. Enable auto-quarantine on the repository and Mergify takes the flaky ones out of the way as it finds them: they still run, their results still land in the dashboard, their failures just stop deciding whether your pipeline is red.

What you need before you start

  • A test suite that can write a JUnit XML report. Every major runner does this, usually behind one flag.
  • A Mergify account with the GitHub App installed, and a MERGIFY_TOKEN stored as a CI secret.

You do not need to touch a single test. Detection reads the reports your CI already produces, and quarantine is a decision applied on Mergify's side, not an annotation in your code. If you want the background on why quarantine beats skipping or deleting, the test quarantine guide covers the tradeoffs.

Step 1: Send your test results to Mergify

Detection needs the failures, so the upload has to happen whether the suite passed or not. In GitHub Actions that means two changes to the job that runs your tests:

- name: Run tests
  id: tests
  continue-on-error: true
  run: pytest --junit-xml=report.xml

- name: Mergify CI Upload
  if: success() || failure()
  uses: Mergifyio/gha-mergify-ci@v25
  with:
    token: ${{ secrets.MERGIFY_TOKEN }}
    report_path: report.xml
    test_step_outcome: ${{ steps.tests.outcome }}

continue-on-error: true on the test step is what makes quarantine possible later. It hands the pass or fail decision to the Mergify step, which is the only step that knows which failures came from quarantined tests. Passing test_step_outcome covers the nastier case where the runner crashes and leaves a report that looks clean.

Pytest users can skip the report plumbing entirely: the pytest-mergify plugin uploads results on its own once MERGIFY_TOKEN is in the environment. Buildkite and the language-specific setups are in the Test Engine docs.

Step 2: Let detection classify your tests

Once reports are flowing, Mergify classifies each test from its results across runs, weighting recent ones more heavily. Only the tests behaving badly show up:

StatusWhat it meansWhat to do
FlakyPasses and fails on the same commit, with no code change between runs.Auto-quarantine handles these by default. Fix them in impact order.
BrokenFails consistently, with recent runs weighted more heavily than old ones. A test that started failing this week is broken, not flaky.Quarantine is opt-in here, because a broken test is usually telling you something true. Turn it on when broken tests keep the pipeline red.
HealthyNothing unusual in the recent history.Not listed in Detection at all. You only ever look at the tests that misbehave.

Two numbers sit next to each test. Confidence says how much data Mergify has: low confidence means the verdict could still move, so give a fresh setup a few days before you act on it. Impact counts the failed executions a test caused, which is the honest measure of what it costs you. Sorting by impact usually shows the same thing in every repository, that a handful of tests are responsible for most of the red builds.

If you would rather fix than quarantine, the framework-by-framework breakdowns in the flaky tests guide name the usual culprits: shared state across parallel workers, frozen clocks, mocks that outlive their test, ordering assumptions that only hold on your laptop.

Step 3: Turn on auto-quarantine

Auto-quarantine is a per-repository switch on the Mitigation page of the dashboard. Enable it and Mergify quarantines flaky tests as detection finds them, without anyone filing a ticket first. Quarantining known-broken tests is a second option next to it, deliberately separate: a consistently failing test is usually reporting something real, and you should decide to ignore it rather than drift into it.

A quarantined test still runs and still shows its full failure history in the dashboard. What changes is the conclusion your CI reports. Quarantine applies on every branch, not only the default one, so a pull request author gets the benefit immediately.

When you want to handle one test by hand, the CLI does it from the terminal or from a script:

# Quarantine a test by its fully qualified name.
mergify tests quarantines add \
  --reason "flaky under xdist, tracked in MRGFY-1234" \
  "tests/auth/test_login.py::test_login_timeout"

# See everything currently quarantined, and why.
mergify tests quarantines list

# Take it back out once it is fixed.
mergify tests quarantines remove \
  "tests/auth/test_login.py::test_login_timeout"

The same commands are worth wiring into an AI coding agent. mergify tests show returns a test's health as an exit code, so an agent that hits a red test can ask whether it is flaky before it spends an hour rewriting code that was never broken.

What this changes in your merge queue

Nothing in your queue config, which is the point. The queue keeps gating on the same CI check it always did:

queue_rules:
  - name: default
    merge_conditions:
      - check-success=ci

pull_request_rules:
  - name: Queue approved pull requests
    conditions:
      - "#approved-reviews-by>=1"
    actions:
      queue:
        name: default

That check is now reported by the Mergify upload step, so it goes green when the only failures came from quarantined tests. A flaky test that used to knock a pull request out of the queue, send it to the back, and burn a full CI run on the retry stops doing any of that. If you have not set the queue up yet, the merge queue guide walks through how it works and how to configure one. Quarantine also pairs with auto-retry: retry buys you a second opinion on a suspicious failure, quarantine is what you reach for once the test has proven it cannot give one.

Step 4: Keep the quarantine list honest

The failure mode of any quarantine system is that it becomes a graveyard. Tests go in, nobody looks again, and a year later a quarter of the suite is decorative. Two habits prevent it.

Put a real reason on every manual quarantine, with a ticket reference. Six months later "flaky" tells nobody anything, while "flaky under xdist, tracked in MRGFY-1234" tells them where to look. Then review the list on a fixed cadence, monthly is enough for most teams. Mergify records whether each quarantined test has recovered, which turns the review into a short list of tests you can take back out rather than an audit of everything.

Watch the trend too. A quarantined test that goes from failing 5% of the time to 40% is telling you something changed, even though it is no longer blocking anyone. That is the part CI Insights is for: quarantine removes the interruption, the data stays.

FAQ

How does Mergify know a test is flaky?

From its history, not from a single run. Mergify compares results for the same commit across CI runs: a test that passes on one run and fails on another without a code change is classified as flaky. A test that fails consistently, with recent runs weighted more heavily, is classified as broken instead.

Does a quarantined test stop running?

No. It runs exactly as before and its results keep flowing into Test Engine. The only thing that changes is that its failures no longer mark the pipeline as failed, so they no longer block a merge.

How long before detection has enough data?

It depends on how often your suite runs. Each test carries a confidence level that reflects how many runs Mergify has collected for it. Treat low-confidence results as preliminary and give a new setup a few days of normal traffic before acting on them.

Can I quarantine a test from a script or an AI coding agent?

Yes. The Mergify CLI has mergify tests quarantines add, remove, and list, and mergify tests show reports a test's health through its exit code (0 healthy, 1 flaky, 6 broken). An agent that hits a failing test can check the health first and quarantine it instead of chasing a phantom regression.

Will auto-quarantine hide a real regression?

It can, which is why only flaky tests are quarantined automatically by default and quarantining broken tests is a separate option you turn on deliberately. Every quarantine keeps its reason and its history, and recovered tests are flagged, so the list stays reviewable rather than becoming a place tests go to die.

Does this work outside GitHub Actions?

Yes. Buildkite has its own integration, and the Mergify CLI uploads a JUnit report from any CI that can run a command. The detection and quarantine behaviour is the same whichever way the results arrive.

Last updated:

Stop chasing flaky tests

Upload your test results, see which tests are actually flaky, and let Mergify quarantine them. Free for up to 5 users.