Testing and Release Boundaries
Contract Tests and Useful Properties
Test stable behavior at boundaries and use properties where examples cannot cover the input space.
Lesson 5 of 6 in the recommended order · About 25 min (estimate)
On this page
Outcome
Write a contract test and articulate a property that remains true across generated inputs.
Why it matters
At this level, code is judged by how safely it changes under load, failure, and team ownership. The technique in this lesson makes an important boundary visible enough to test and review.
Concept
Example tests document named cases. Property tests describe invariants across many cases. Contract tests keep every adapter honest against the same observable behavior.
Read the code
def normalize_tags(tags):
return sorted({tag.strip().lower() for tag in tags if tag.strip()})
def assert_normalizer_contract(normalize):
first = normalize([" Python ", "python", "API"])
second = normalize(first)
assert first == ["api", "python"]
assert second == first
assert_normalizer_contract(normalize_tags)
print("contract passed")
Read from the public behavior inward: identify the input boundary, the decision, and the observable result before studying syntax.
Predict the output
Predict the exact output before running the example.
Check your prediction
It prints contract passed. The second assertion captures idempotence: normalizing an already normalized result changes nothing.
Modify the code
Change one valid input into the nearest invalid or overloaded case. Write down which layer should reject it and what the caller should observe.
Review the change
Keep the failure at the narrowest boundary that owns the rule. Preserve a stable return value or exception contract so callers do not need to inspect implementation details.
Debug the bug
A test that duplicates the implementation with the same loop can repeat the same mistake. Assert externally meaningful examples and invariants instead.
Try it yourself
Complete the focused implementation and run its deterministic checks.
Loading this exercise…
Practical challenge (optional)
Add properties for order independence and blank-input removal, then test a deliberately faulty normalizer to prove sensitivity.
Sign in to track your progress on this exercise.
AI collaboration
Checkpoint
- What does an example test communicate that a property test does not?
- What property proves this normalizer is idempotent?
- Why is reimplementing the production loop inside the test weak evidence?
Answers
- It names a concrete input and expected output that serves as an understandable behavioral example.
- For every valid input
x,normalize(normalize(x)) == normalize(x). - The test can repeat the same algorithmic mistake and agree with broken production code. Assert the external contract instead.
Sign in to track your progress on this exercise.
Summary and next step
You made the boundary explicit, predicted its behavior, tested a deterministic implementation, and examined its failure mode. Continue to the next lesson to combine this technique with a wider application or production constraint.