All posts
Test management

How to find flaky tests: a five-step method for CI

A flaky test passes and fails on the same code. To find them, keep every attempt (retries included) tied to its commit, list the tests that mix passes and failures in the last 30 days, rank them by failures, and confirm each by rerunning it in isolation.

Table of contents
  1. What counts as a flaky test?
  2. How do I find flaky tests in my suite?
  3. How do I confirm that a test is really flaky?
  4. Should I just turn on automatic retries?
  5. Should I quarantine, fix or delete a flaky test?
  6. Where Probara fits
  7. FAQ

You find flaky tests by looking at history, not at a single red build: a test is flaky when it both passes and fails on the same code. Record every attempt of every test, including retries, then list the tests whose results mix passes and failures on the same commit. Google measured that about 84% of the pass-to-fail transitions in its CI involved a flaky test (Google Testing Blog), so this list is usually where most of your "broken build" noise comes from.

What counts as a flaky test?

A flaky test is a test that can pass and fail without any change to the code or to the test. Google's definition is "a test that exhibits both a passing and a failing result with the same code" (Google Testing Blog). The same post reports that about 1.5% of all its test runs returned a flaky result, and that almost 16% of its tests had some level of flakiness.

Two conditions make the definition useful in practice:

  • Same code. A test that started failing after a commit and keeps failing is a regression, not a flaky test. The commit SHA (or build) is what lets you tell them apart.

  • Mixed outcomes. One failure proves nothing. You need at least one pass and at least one failure on that same code, either across runs or across retries of the same run.

The problem is common outside Google too. A February 2026 study of GitHub Actions found that 3.2% of builds were rerun, that 67.73% of those reruns were flaky (they failed, then passed with no change), and that flaky tests caused 64.99% of the flaky failures, ahead of network problems at 15.8% (Ge and Zhang, arXiv 2602.02307).

How do I find flaky tests in my suite?

Find flaky tests with five steps: keep every attempt, tie it to a commit, query for mixed outcomes, rank, and confirm.

  1. Keep every attempt. Turn on a small number of retries in CI (one or two) and make sure your reporting keeps each attempt as its own result. If only the final outcome is stored, a test that failed and then passed on retry looks green, and the evidence is gone.

  2. Tie each result to the code it ran against. Store the branch, commit SHA and CI build URL with every run. Without them you cannot tell "failed on the same code" from "failed after a change".

  3. Query for mixed outcomes. List the tests that have at least one failure and at least one pass in a recent window, such as the last 30 days, on your main branch. Two signals count: a failure followed by a pass on retry in the same run, and a pass and a failure in different runs of the same commit.

  4. Rank by cost. Sort the list by how often each test failed in the window. A test that flaked 12 times in a month costs more reruns and more trust than one that flaked once, so it goes to the top.

  5. Confirm before you label. Rerun each suspect many times in isolation (next section). A test that fails only in the full suite but never alone points to shared state or test order, which is a different fix from a timing problem.

Run the query on a schedule, for example weekly, rather than once. Google notes that its rate of new flaky tests was "about the same as the fix rate" (Google Testing Blog), so a one-off cleanup does not stay clean.

How do I confirm that a test is really flaky?

Confirm a suspect by running it many times on unchanged code and counting failures. Most runners can do this directly:

# Playwright: run each matching test 50 times
npx playwright test tests/checkout.spec.ts --repeat-each=50

# Playwright: fail the CI job when any test is flagged as flaky
npx playwright test --retries=2 --fail-on-flaky-tests

The Playwright CLI documents --repeat-each <N> as "Run each test N times" and --fail-on-flaky-tests as "Fail if any test is flagged as flaky" (Playwright CLI).

Read the outcome this way:

Result of 50 isolated runs

What it suggests

Next step

50 passes, but it fails in the full suite

Order dependency or shared state (data, cache, globals)

Run it after the tests that precede it in CI; reset state between tests

Some failures in isolation

Timing, concurrency or an external dependency

Read the errors of failed attempts side by side; look for timeouts and waits

50 failures

Not flaky: it is broken on this code

Treat it as a regression

50 passes, and it passes in the suite too

Not reproducible here; often CI-only resources

Compare CI and local environments (CPU, network, data)

Google lists the usual root causes as concurrency, reliance on non-deterministic or undefined behavior, flaky third-party code and infrastructure problems (Google Testing Blog). Compare the error messages of failed attempts: a timeout on the same locator every time is a very different lead from a different assertion each time.

Should I just turn on automatic retries?

Turn on retries to collect evidence and keep pipelines moving, but never treat a retry that passed as a pass you can ignore. Each framework has its own switch:

Framework

How to retry

What it records

Playwright

retries: 2 in playwright.config.ts, or --retries=2

Labels each test "passed", "flaky" (failed first, passed on retry) or "failed" (Playwright)

Jest

jest.retryTimes(3); add { logErrorsBeforeRetry: true } to log the failures

Only available with the default jest-circus runner (Jest)

pytest

pytest --reruns 5 --reruns-delay 1 or @pytest.mark.flaky(reruns=5) with the pytest-rerunfailures plugin

Plugin version 16.7, released September 2026 (PyPI)

The risk is that retries hide real bugs. Google describes marking a test as flaky so that it reports a failure only after 3 failures in a row, and calls the result "hardly a perfect solution": it reduces false positives but encourages developers to ignore flakiness, and a real break in a 15-minute flaky-marked test takes 45 minutes to surface (Google Testing Blog).

A practical rule: allow one or two retries, keep every attempt, and review the "failed then passed" list as part of the weekly query from the previous section.

Should I quarantine, fix or delete a flaky test?

Fix the flaky tests that guard important behavior, quarantine the ones you cannot fix this week, and delete the ones nobody would miss. Quarantine means the test still runs but no longer blocks the pipeline; Google built a tool that quarantines tests whose flakiness is too high and files a bug for the owner, while warning that this "could easily mask a real race condition" (Google Testing Blog).

Situation

Decision

Covers a critical path (checkout, login, billing) and the cause is known

Fix now

Flakes often, cause unknown, blocks other teams

Quarantine, with an owner and a review date

Duplicates coverage another stable test already gives

Delete

Quarantined for weeks with no pass at all

Treat it as broken: fix or delete

Whatever you choose, label it in one place. A tag or a flag on the test case lets you count quarantined tests, see how long they have been there, and notice when one of them stops passing completely.

Where Probara fits

Probara keeps the attempt history that the method above depends on. Its reporters for Playwright, Jest, Vitest and Cypress send every attempt as its own result, with its own status, duration and errors: a test retried twice sends three results. The run shows the last outcome, and the earlier attempts stay in the case's history (Playwright results and retries). Each run also records the branch, commit and CI build URL.

Run case panel showing a failed attempt that timed out on the 3-D Secure step, with the Retries tab next to Execution

On Pro and Enterprise plans, Queries turn step 3 into a saved query. This one lists automated cases with both a failure and a pass in the last 30 days:

FROM cases WHERE automated = true
  AND exists(results where status = failed AND executed > now(-30d))
  AND exists(results where status = passed AND executed > now(-30d))
ORDER BY title

Each test case has an is flaky field, which you can count on a dashboard with the Flaky test cases widget (Dashboards) and query with isFlaky. The PQL reference includes a query for flagged cases with no pass in the last 30 days, the "quarantined for weeks" row of the decision table. For a broader view of what a test management tool records, see What is a test management tool?.

FAQ

What is the difference between a flaky test and a failing test?

A failing test fails every time on the same code; a flaky test passes on some runs and fails on others with no change. Rerunning on the same commit tells them apart.

How many reruns does it take to prove a test is flaky?

One pass and one failure on the same code is enough to call it flaky. To estimate how often it flakes, run it in isolation many times, for example with Playwright's --repeat-each=50, and count the failures.

Do retries make flaky tests worse?

Retries do not cause flakiness, but they can hide it and the real bugs behind it. Keep every attempt, and review the tests that failed and then passed on retry instead of counting them as green.

What causes most flaky tests?

Google lists concurrency, non-deterministic or undefined behavior, flaky third-party code and infrastructure problems. In GitHub Actions, a 2026 study attributed 64.99% of flaky failures to flaky tests and 15.8% to network issues.

Should flaky tests block a release?

A flaky test on a critical path should block until someone confirms the failure is not real. A quarantined test should not block, but it needs an owner and a date to come back.

Try it on your own test suite

Create an organization, import your cases and run your first suite this afternoon.