metamorphic-testing
Skill0 starsChanged 3 months agoMITView on GitHub
This skill should be used when the user asks "how do I test this when I do not know the expected output", "how do I test a search ranking or ML model", "what is the oracle problem", "how do I test a scientific computation or optimizer", or is testing code whose correct answer cannot be computed independently. Covers testing without a known correct answer. For judging whether existing tests are strong use `mutation-testing`.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- khgouldy/skillstheir own version, first seen Oct 11, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- khgouldy/skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository29 other files it gives its agents.
- algorithmic-complexity-attacksSkill
- cache-stampedeSkill
- clocks-and-timeSkill
- code-hotspotsSkill
- connascenceSkill
- connection-poolingSkill
- consistent-hashingSkill
- coordinated-omissionSkill
- cyclomatic-complexitySkill
- database-queuesSkill
- dependency-confusionSkill
- expand-contractSkill
- floating-pointSkill
- hyrums-lawSkill
- index-designSkill
- isolation-anomaliesSkill
- keyset-paginationSkill
- module-depthSkill
- mutation-testingSkill
- mvcc-and-bloatSkill
- parse-dont-validateSkill
- query-plansSkill
- queueing-and-utilizationSkill
- retry-amplificationSkill
- start-hereSkill
- tail-latencySkill
- unicode-pitfallsSkill
- universal-scalability-lawSkill
- write-amplificationSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 22
Do not assert what one output is.
- Distinctive: line 37
Ask what transformations of input should have a predictable effect on output.
- Distinctive: line 83
Ask these in order:
- Very specific: line 100
Use mutation-testing to check whether these relations actually catch injected errors.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Metamorphic testing
The oracle problem
Ordinary testing needs a known correct answer:
assert add(2, 2) == 4Much real code has no such oracle. What is the correct output of a search ranking, a route planner, a compiler optimization, a recommendation model, a physics simulation, a float-heavy numerical routine? Computing the expected answer independently would mean writing the program a second time.
The usual responses both fail. Asserting the current output freezes today's bugs as the specification (a golden test that certifies whatever it does now). Asserting only that it does not crash tests almost nothing.
The technique
Do not assert what one output is. Assert how outputs relate when the input changes in a known way. That relation is a metamorphic relation.
# no oracle, but a relation holds
results_a = search("machine learning")
results_b = search("machine learning") # extra whitespace
assert results_a == results_b # normalization relationThe relation is checkable without knowing the right answer. When it breaks, there is a real bug.
This found hundreds of real bugs in GCC and LLVM (EMI testing), in SQL engines, and in commercial machine translation.
Relation families
Ask what transformations of input should have a predictable effect on output.
| Family | Relation | Example |
| Invariance | Output unchanged | Reordering an unordered input, renaming variables, adding whitespace, permuting independent records |
| Equivalence | Different route, same result | sum(xs) equals sum(reversed(xs)) within tolerance, two query plans, two code paths for the same operation |
| Monotonicity | Output moves in a known direction | Adding a matching document does not lower its corpus's result count. Raising a price does not raise predicted demand |
| Inverse | Round trip returns the original | decode(encode(x)) == x, decompress(compress(x)) == x, parse(format(x)) == x |
| Subsumption | A narrower query returns a subset | search("red shoes") results are a subset of search("shoes") results |
| Scaling | A proportional input change scales the output | Doubling every weight leaves the argmax unchanged |
| Additivity | Parts compose to the whole | mean(a + b) lies between mean(a) and mean(b) |
Worked examples
Compiler: transform a program by inserting statements into provably dead code. The compiled output must behave identically. This is EMI (Equivalence Modulo Inputs) testing and it found hundreds of GCC and LLVM bugs.
Database: rewrite a query into a form guaranteed to be equivalent (add a redundant WHERE TRUE, reorder joins, push a predicate). The result set must be identical. This is how SQLancer finds engine bugs.
Machine translation: replace one noun with another noun of the same type. The sentence structure of the translation should stay the same. Divergence indicates a bug.
Numerical code: solve(A, b) should satisfy A @ x ≈ b. Checking the residual needs no oracle and no reference implementation. Also scale the system by a constant and confirm the solution scales.
Search or recommendation: adding an irrelevant document must not change the ranking of existing results.
Combining with property-based testing
Metamorphic relations are properties, so a property-based framework generates the inputs and shrinks failures automatically:
from hypothesis import given, strategies as st
@given(st.lists(st.integers()))
def test_sort_is_permutation_invariant(xs):
assert sorted(xs) == sorted(list(reversed(xs)))
@given(st.text())
def test_encode_decode_round_trip(s):
assert decode(encode(s)) == sTools: Hypothesis (Python), fast-check (JS), QuickCheck and its ports, proptest (Rust), jqwik (Java).
Shrinking is the main practical benefit: a failure arrives as the smallest input that still breaks the relation, which is usually enough to diagnose it directly.
Finding relations
Ask these in order:
- What input changes should change nothing? These are the strongest and easiest relations.
- What operation undoes this one? Round trips catch a great deal.
- What input change should move the output in a known direction, even without a known magnitude?
- What two different ways exist to compute the same thing? A slow obviously-correct implementation is an excellent oracle for a fast one.
- What does the domain guarantee regardless of input? Conservation laws, totals matching, non-negativity, sums to one.
Item 4 deserves emphasis. Where a naive O(n²) reference implementation is easy to write, testing the optimized version against it is the strongest available test, and the reference need never ship.
Limits
- Relations can hold on broken code. A function returning a constant satisfies many invariance relations. Combine relations, and combine them with example-based tests where an oracle does exist.
- Tolerances need care in floating-point relations.
sum(xs) == sum(reversed(xs))is false for floats. Use a tolerance derived from the computation. Seefloating-point. - A failing relation identifies a bug, not its location. It says the relation broke, not which of two runs is wrong.
- Some relations are approximate by design, for example a translation whose structure "should usually" be preserved. Approximate relations produce false positives and need a threshold, so state it explicitly.
Use mutation-testing to check whether these relations actually catch injected errors. A metamorphic test that kills no mutants asserts a relation too weak to matter.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

