Skip to content

Handbooks/Software that stays in use

2Reviewed 14 Oct 2026

Tests/Fail for the right reason

Fail for the right reason

A suite keeps a change cheap only when a red test means the behaviour changed. A test that goes red because the structure moved, or that stays green when the behaviour is wrong, trains the team to stop trusting red.

My operating rule: if the behaviour held, delete the test or rewrite it against the behaviour. An agent-written test stays a draft until a person has seen it fail for a behaviour they can name.

Why red stops meaning a fault

Fowler's self-testing code is a bug detector you can run in one command. He says you are not doing continuous integration without it. The benefit he names is the confidence to change the system. Without that detector, a fix can create more faults than it removes, and people stop touching the code that needs the change.

That confidence depends on red meaning a fault. Alex Eagle names the failure mode a change-detector test. It breaks when the production code changes, and it does not check the behaviour of either version. He says these tests provide negative value. They catch no defects, and keeping them slows the work. Rewrite them, or delete them.

I treat a red that nobody can tie to a behaviour as that detector. People learn to edit the test and move on. The next real fault looks the same, and the suite no longer protects the change.

Fowler, self-testing code·Eagle, change-detector tests

Sensitive to the behaviour

Beck names the pair I use. Behavioural: if the behaviour changes, the test result should change. Structure-insensitive: the test should not change its result if the structure of the code changes. The behaviour moved, so the test moved. The structure moved and the behaviour held, so the test stayed green.

Meszaros, on the xUnit Patterns site, defines a fragile test as one that fails when the system changes in a way that does not affect the part the test is exercising. He separates the case that looks similar and is not fragility. If the functionality the test verifies was modified, that failure is the reason for the regression test. He calls a further case overspecified software: the test says how the software should do the work, not what it should achieve, and it passes only for one implementation.

Beck, test desiderata·Meszaros, fragile test

When a test goes red

This is the order I use when a test in the commit build goes red, and when someone adds a test.

  1. 01 NameThe behaviour, in the words a caller would use.
  2. 02 AskDid that behaviour change?
  3. 03 KeepIf it changed, fix the code or accept the new behaviour on purpose.
  4. 04 DropIf it held, delete the test or rewrite it against the behaviour.
  5. 05 ProveIf you have not seen it fail, break the behaviour and watch the red.
A red test stays when it names a behaviour change. Otherwise it goes.
  1. Name the behaviour the way a caller or a user would. If the only description is a method name, a private collaborator, or the order of calls, the test is already tied to the structure.
  2. If that behaviour changed, the red is the point. I fix the code, or I accept the new behaviour and change the assertion on purpose. Meszaros is explicit that a failure of the functionality the test verifies is the regression, not the fragile case.
  3. If the behaviour held, I do not edit the assertion to match the new structure and call the suite healthy. Eagle's remedy is rewrite or delete. I rewrite the test against the behaviour, or I delete it.
  4. Fowler's cycle for a new behaviour is red, green, refactor. You write the test before that behaviour exists, so the test starts red, and then you write the code until it passes. An agent that writes the test against code that already works skips that red. I break the behaviour on purpose, watch the test go red, restore the behaviour, and watch it go green. Until a person has done that, the test is a draft, and I do not count it as a gate.
  5. The security checks stay while I delete change-detectors. The SDLC field card's hook is the line I use: a check that cannot fail is not a gate. A check I rely on still has to fail the build when the requirement is broken. If the requirement still holds and the check went red because a tool's output changed shape, I rewrite it against the requirement or I delete it.

Meszaros, fragile test·Eagle, change-detector tests·Fowler, red, green, refactor·SDLC field card

What I inspect

These are the questions I ask of a suite. They are my check, not a list Beck, Eagle, or Meszaros published.

  1. When this test last went red, what behaviour changed?
  2. If I break that behaviour on purpose, does this test go red?
  3. Does the test name a result someone can observe, or a call order and a private collaborator?
  4. Which tests did an agent add, and which of those has a person seen fail?
  5. Which security checks can fail the build, and which cannot?

How I would know

I keep two counts. Among tests that went red, how many failed because the behaviour changed, and how many failed because the structure moved or the setup around the test changed. Among tests an agent added, how many a person has seen go red for a behaviour they can name. The practice is a suite that stays green when the structure moves and goes red when the behaviour changes. A larger suite, or a green badge, is not the count.

Where I do not use it

I do not delete a test because the red was inconvenient. If the behaviour it verifies actually changed, that failure is the regression Meszaros says the test is for. I change the assertion only when I am accepting a new behaviour on purpose.

I do not use the delete rule on a test whose job is to notice behaviour I cannot name yet. When I can name it, I rewrite the test against that name, or I drop the test.

A test that fails for the right reason and takes too long is a different problem. Fowler puts that check in a later stage of the build, not in the commit build. I do not delete it to buy the ten minutes.

I do not install the rule where there is no self-testing build. Fowler's detector is a command you can run. A test that never runs cannot fail for any reason.

Meszaros, fragile test·Fowler, the later stage·Fowler, self-testing code

References