Skip to main content
Learning Center
Python Programming

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

  1. What does an example test communicate that a property test does not?
  2. What property proves this normalizer is idempotent?
  3. Why is reimplementing the production loop inside the test weak evidence?
Answers
  1. It names a concrete input and expected output that serves as an understandable behavioral example.
  2. For every valid input x, normalize(normalize(x)) == normalize(x).
  3. 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.

learning.goultergroup.com

The interactive parts of this page have not loaded. Reading and links still work; reload the page to try again.