A metamorphic test checks how an output should change after a controlled input transformation.
Python testing: check a reconciliation relation without copying the implementation
Operation contract
The receipt-total function rejects booleans and negative amounts, then sums bounded entries. The test changes the order of a batch and checks the total remains equal. It also appends one accepted amount and checks that the total rises by exactly that amount. Neither property requires a second copy of the sum loop as an oracle. The test still checks an explicit small expected value to catch a function that always returns zero.
Failure and ownership boundary
These relations cannot prove every accepted input is correct. A faulty function can satisfy both properties on a narrow set. Pair them with independently chosen examples, rejected-input cases and mutation checks. Hypothesis property tests: compare generated cases with an independent contract, Python mutation testing: prove a suite rejects selected wrong implementations and Python refactoring: preserve rejected inputs as well as accepted results extend that evidence.
Working program
def receipt_total(amounts):
if type(amounts) is not list or len(amounts) > 20 or any(type(amount) is not int or not 0 <= amount <= 1000 for amount in amounts):
raise ValueError("bounded minor units")
return sum(amounts)
batch = [125, 75, 50]
base = receipt_total(batch)
print("known total:", base)
print("reorder invariant:", receipt_total(list(reversed(batch))) == base)
print("append relation:", receipt_total(batch + [30]) == base + 30)
try:
receipt_total([True])
except ValueError:
print("boolean amount rejected")Output
known total: 250
reorder invariant: True
append relation: True
boolean amount rejectedCosts and limits
Summing n bounded integers takes O(n) time and bounded accumulator size here. A test that creates many permutations has factorial growth, so the fixture checks one transformation and relies on separate randomized or exhaustive suites for broader coverage.
Common Mistakes
- A relation alone may accept a constant or selectively wrong implementation.
- Do not duplicate the production algorithm and call it an independent oracle.
Connected lessons
Hypothesis property tests: compare generated cases with an independent contract, Python mutation testing: prove a suite rejects selected wrong implementations, Python refactoring: preserve rejected inputs as well as accepted results.
