Test quality and test oracles
How to tell whether tests catch real bugs, from test oracles and code coverage to mutation testing and property-based testing.
Start here
What is code coverage?
Code coverage is the share of a program's code that runs during its tests, which shows what was executed but not whether the tests checked the results.
Concepts
What is a test oracle?
A test oracle is the source of truth a test checks a result against, e.g. a specification that states the correct output for a given input.
What is mutation testing?
Mutation testing checks a test suite by making small deliberate bugs in the code, called mutants, and counting how many of them the tests catch.
What is property-based testing?
Property-based testing states rules that should hold for every input, then checks them against many generated inputs and shrinks any failure it finds.
What are catching tests (catching JiTTests)?
Catching tests are tests generated for a specific change and meant to fail if that change introduced a bug, rather than to guard the code for years.
What is a fail-before, pass-after check?
A fail-before, pass-after check runs a new test on old code, where it must fail, then on the changed code, where it must pass, to show it catches the bug.
What is a tautological test?
A tautological test cannot fail because its expected value comes from the same code, mock, or constant as its actual result, so it passes on wrong code.
What is adversarial testing in software?
Adversarial testing is deliberately trying to break software with unexpected, hostile, or careless input and actions, instead of confirming the happy path.
What is AI test case generation?
AI test case generation uses a language model to write tests from code or specs, and some tools then drop tests that fail, flake, or add no coverage.
What is an assertion in testing?
An assertion is a statement in a test that checks a condition, e.g. that a result equals its expected value, and fails the test if the check is false.
What is an equivalent mutant?
An equivalent mutant is a code change that behaves exactly as the original code does, so no test can ever kill it and it lowers the mutation score.
What is assertion coverage?
Assertion coverage measures how much of a program's behavior its tests check with assertions, rather than how many lines the tests cause to run.
What is chaos engineering?
Chaos engineering is running controlled experiments that inject failures, e.g. a killed server, to learn how a system copes before a real outage happens.
What is differential testing?
Differential testing gives the same inputs to two or more implementations, or two versions of one program, and treats any difference as a possible bug.
What is fuzzing?
Fuzzing is feeding a program large numbers of random or malformed inputs to find the crashes, hangs, and input bugs that ordinary tests miss.
What is golden file testing (golden master)?
Golden file testing compares a program's output with a saved, approved copy called the golden file, and fails when anything in the output changes.
What is incremental (diff-scoped) mutation testing?
Incremental mutation testing retests only the mutants a pull request could affect, so CI can check the change's tests in far less time than a full run.
What is metamorphic testing?
Metamorphic testing checks how outputs should change when inputs change in a known way, so code can be tested when the exact right answer is unknown.
How-to guides
How to check that AI-written tests catch bugs
Checking AI-generated tests means showing that they fail on broken code, by running them on the old code and adding small bugs on purpose.
Comparisons
What is the difference between mutation score and code coverage?
Mutation score is the share of small injected bugs that a test suite catches, while code coverage is the share of code that runs during its tests.
What is the difference between mutation and property-based testing?
Mutation testing checks how well a test suite catches injected bugs, while property-based testing checks the code itself against rules over many inputs.
Common questions
How many tests is too many?
Test bloat is the growth of a test suite beyond the behavior it checks, which adds run time, upkeep, and review work without catching more bugs.
Terms in this topic
- Adversarial testing
- Adversarial testing is a testing approach that deliberately tries to break software with unexpected, hostile, or careless inputs and actions, instead of confirming the expected path.
- AI test case generation
- AI test case generation is the use of a language model to write test cases from code or a specification. Some tools then drop tests that fail to build, fail, flake, or add no coverage.
- Assertion
- An assertion is a statement in a test that checks a condition and fails the test when the condition is false.
- Assertion coverage
- Assertion coverage is a test measure of how much of a program's behavior the tests check with assertions, as opposed to how many lines run while the tests execute.
- Branch coverage
- Branch coverage is a code coverage measure that counts which outcomes of each decision point, e.g. both sides of an if statement, the tests have run.
- Catching test
- A catching test is a test that is generated for one code change and designed to fail if that change introduced a bug, then usually thrown away after review.
- Chaos engineering
- Chaos engineering is the practice of running controlled experiments that inject faults into a system, e.g. a stopped server, to learn how the system copes before a real outage.
- Characterization test
- A characterization test is a test that records what existing code does now, right or wrong, so later changes can be checked against that recorded behavior.
- Code coverage
- Code coverage is a test measure that reports the percentage of a program's code that runs during its tests, which shows what was executed but not what was checked.
- Coverage-guided fuzzing
- Coverage-guided fuzzing is a fuzzing method that keeps the inputs which reach new code paths and mutates them further, so the fuzzer explores a program efficiently.
- Differential testing
- Differential testing is a technique that gives the same inputs to two or more comparable implementations, or two versions of one program, and treats any difference in output as a possible bug.
- Equivalent mutant
- An equivalent mutant is a code change made during mutation testing that behaves exactly the same as the original program, so no test can ever detect or kill it.
- Fail-before, pass-after check
- A fail-before, pass-after check is a test of a test that must fail on the code before a change and pass on the changed code.
- Fault seeding
- Fault seeding is a technique that deliberately inserts known bugs into code to see how many of them a test suite or review process detects.
- Fuzzing
- Fuzzing is a testing technique that feeds a program large numbers of random or malformed inputs to find crashes, hangs, and bugs in input handling.
- Golden file
- A golden file is a saved, approved copy of a program's output that a test compares new output against, failing when anything in the output changes.
- Implicit oracle
- An implicit oracle is a general failure signal that marks a result as wrong without any expected value, e.g. a crash.
- Incremental mutation testing
- Incremental mutation testing is mutation testing that tests only the mutants a change could affect, e.g. those in the lines of a pull request, instead of every mutant in the codebase.
- Metamorphic testing
- Metamorphic testing is a technique that checks how a program's output should change when its input changes in a known way, so code can be tested without an exact expected output.
- Mutant
- A mutant is a copy of a program with one small deliberate change, e.g. a flipped comparison, that mutation testing uses to check whether the tests fail.
- Mutation score
- Mutation score is a test-strength measure that reports the share of valid mutants a test suite detects, which shows how well the tests check behavior.
- Mutation testing
- Mutation testing is a technique that checks a test suite by injecting small deliberate bugs, called mutants, and counting how many of them the tests catch.
- Partial oracle
- A partial oracle is a test oracle that checks some properties of a result when the full expected result is unknown, e.g. that an order total is never negative.
- Property-based testing
- Property-based testing is a technique that states rules that should hold for every input, then checks them against many generated inputs and shrinks any failing input.
- Shrinking
- Shrinking is a step in property-based testing that reduces a failing generated input to the smallest input it can find that still fails, so the bug is easier to understand.
- Tautological test
- A tautological test is a test that cannot fail because its expected value comes from the same code, mock, or constant as the result it checks. It passes whether the code is right or wrong.
- Test oracle
- A test oracle is the source of truth that a test compares a result against, e.g. a specification that states the correct output for a given input.
- Test oracle problem
- The test oracle problem is the difficulty of deciding whether a test passed or failed for a given input and state, which is hardest when nobody knows the correct answer.