Write Tests Around Boundaries, Not Implementation Details

Use the cent below, at and above a shipping threshold to specify behavior without coupling tests to helper calls.

Two machined components meeting at a measured boundary.
The behavior at a boundary is part of the contract.
IN THIS ARTICLE+

A shipping calculator offers free delivery at an order total of 50 dollars. A test checks that an internal helper was called exactly twice. A later refactor changes the helper calls while every customer receives the same shipping charge. The test failed, but the product did not.

Turn the rule into a decision table

Specify the currency and representation first. If totals are represented in integer cents, test 4,999, 5,000 and 5,001. State which side includes the threshold. Then decide how discounts, taxes, returns and unsupported currencies affect the eligible total. A case at the threshold is only useful if the input means what the business thinks it means.

Assert the returned shipping charge or customer-visible decision. Avoid verifying internal loops, helper names or intermediate data structures unless those are themselves part of a documented contract.

Use test doubles at real boundaries

Replace an external carrier API with a controlled response to exercise a timeout or rejected address. It can be appropriate to verify that a payment request was submitted once, because the side effect matters. By contrast, mocking a pure helper inside the calculator usually couples the test to the implementation.

Keep at least one integration check for the adapter that translates the real external response. A perfectly simulated dependency can still drift from the service it represents.

Read a failed test as an explanation

Use case names such as “discount drops eligible total below threshold.” Include input and expected outcome in failure output. When a rule changes, the decision table shows exactly which examples need an editorial or product decision. When only the implementation changes, tests anchored to observable behavior can keep passing without being rewritten.

Further reading

Python: unittest

Related guide: Find the First Bad Change in a Software Release.

Comments

No comments yet. Share the first perspective.

Related articles

Software articles →

Categories