liveBook · Manning

Flaky test

As explained in The Art of Unit Testing 3e

Test flakiness is the tendency of tests to produce inconsistent pass-or-fail results without a relevant code change.

A flaky test gives different pass-or-fail results even though the production code has not changed. It may fail, pass on a rerun, and fail again later. That makes a failure hard to interpret: it might reveal a real bug, or it might be noise from the test itself or its environment. A trustworthy test should fail for a meaningful reason. section 7.2.5 section 7.2

Flakiness is not the same as an ordinary failing test. A real production bug is a useful failure. A buggy, outdated, conflicting, or flaky test is untrustworthy until investigated. Calling every failure “flaky” is especially risky because it gives real defects an easy excuse to be ignored. section 7.2 section 7.4.3

Shared state can make parallel tests unreliable

Suppose tests share one verifier instance or an errors array outside their setup. One test can change that shared state while another test runs, so the outcome depends on timing. Create a fresh verifier and fake rule for each test instead. In Jest, parallel execution makes shared upper-scope state particularly hazardous. section 2.6 section 2.6

External and changing inputs are common causes. Current time, random numbers, machine names, databases, networks, configuration, third-party services, and security layers add moving parts. The more real dependencies a test uses, the less control it has over its execution path and the greater its chance of inconsistent results. section 7.4.5 section 7.5

Figure 7.1 The higher the level of the tests, the more real dependencies they use, which gives us confidence in the overall system correctness but results in more flakiness.
07-01

Compare test levels by their dependency trade-off: higher-level tests use more real dependencies, increasing overall confidence but also the opportunity for flakiness. section 7.5

When a test is confirmed flaky, reduce its cost deliberately:

  • Define flakiness consistently, such as running the suite repeatedly without code changes and finding tests with inconsistent outcomes. section 7.5.1
  • Quarantine it in a separate category or pipeline so it does not add noise to regular delivery feedback. section 7.5.1 section 7.6
  • Fix it by controlling dependencies—for example, insert required database data as part of the test. section 7.5.1
  • Convert it to a lower-level test by replacing a real dependency, such as a network endpoint, with a stub. section 7.5.1 section 3.8
  • Kill it when its value does not justify its maintenance cost, especially when better tests already cover the behavior. section 7.5.1

Do not respond by eliminating integration or end-to-end tests altogether. They can provide confidence that lower-level tests cannot. Instead, keep a fast, nonflaky “safe green” area for frequent local feedback, and run higher-level or quarantined checks separately in automated pipelines. A balanced test strategy accepts the trade-off between confidence, speed, maintainability, complexity, and flakiness. section 7.4.3 section 10.5

How The Art of Unit Testing 3e covers it

How deep it goes
Defines inconsistent outcomes, traces dependency and state causes, and gives a fix-convert-kill response process.
Examples
Uses a Jest verifier with beforeEach setup, dynamic values, databases, networks, and a stubbed network endpoint.
What it assumes
Introduces flakiness from zero while assuming only the basic idea that tests pass or fail.
What you can do after
Diagnose/debug with it by identifying inconsistent tests, isolating them, controlling dependencies, converting levels, or removing them.
How it connects
Connects shared state, test doubles, unit tests, integration tests, end-to-end tests, and build pipelines.

Also explained in

Compare how 2 books explain flaky test →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Art of Unit Testing 3e. The section links point to the parts of the book it draws on.