Full teaching narration is free with Private Starter.Create free account
Back to curriculum
Computer ScienceGrade 12· U.S. National — Common Core & NGSS
Aligned to:U.S. educational frameworks

Debugging with Unit Tests and Assertions

Students diagnose defects in a short program, write targeted unit tests and assertions, and use test evidence to evaluate competing fixes for correctness and reliability.

Debugging with Unit Tests and Assertions

Illustrations are auto-generated and may be placeholders. They can be refreshed to match the narration.

Full teaching narration is included free with a Private Starter account.Create free account

From Failure Report to Test Case

A failure report is evidence, not yet a test. Translate it into a repeatable example with a stated expected result. Suppose a store promises, “Orders of $50 or more receive free shipping,” but a customer reports a $5.99 charge on a $50.00 order. Record the input, the required rule, the observed output, and the program version. Then form a test: call shipping_cost(50.00) and expect 0.00. Keep unrelated details, such as the customer’s name, out of the test. A strong test fails for the reported reason and can be run automatically. Before changing code, reproduce the failure. If the test unexpectedly passes, investigate differences in tax handling, coupons, data types, or software versions rather than guessing at a fix.

A failure report is converted into a repeatable shipping-cost test with recorded input, expected output, observed output, and version.
A failure report is converted into a repeatable shipping-cost test with recorded input, expected output, observed output, and version.Source: Illustrated for this lesson

Arrange, Act, and Assert

The Arrange, Act, and Assert pattern makes each test easy to follow. Arrange the input and any required objects: set subtotal to 50.00. Act by calling the function once: set result to shipping_cost(subtotal). Assert that the result equals 0.00. In Python, the key check could be assert result == 0.00. A testing framework can also display a helpful message showing both values when the assertion fails. Each test should focus on one behavior so that a failure has a clear meaning. Assertions may also check internal assumptions, such as a subtotal never being negative. However, programs should use explicit validation for user input because runtime assertions can be disabled. Clear test names, such as test_free_shipping_at_threshold, communicate the rule being verified.

A Python unit test is divided into three connected blocks for arranging a subtotal, calling the function, and checking the result.
A Python unit test is divided into three connected blocks for arranging a subtotal, calling the function, and checking the result.Source: Illustrated for this lesson

Testing Normal and Edge Cases

A reliable test suite includes normal cases, boundary cases, and invalid inputs. For the shipping rule, $80.00 is a normal free-shipping case, while $20.00 is a normal paid-shipping case. The most important boundary values are $49.99, $50.00, and $50.01 because they surround the threshold. Expected results are $5.99, $0.00, and $0.00, respectively. Also test a negative subtotal and define the intended response, such as raising a ValueError. These cases reveal different defects: a strict greater-than comparison fails at exactly $50.00, while missing validation may accept impossible values. Avoid selecting many random examples that exercise the same path. A small, targeted collection of cases provides stronger evidence when each case represents a distinct requirement, branch, boundary, or failure mode.

A number line shows normal, boundary, and invalid shipping subtotals with their expected results.
A number line shows normal, boundary, and invalid shipping subtotals with their expected results.Source: Illustrated for this lesson

Tracing and Isolating the Defect

When the boundary test fails, trace the program from input to output instead of changing code immediately. Suppose shipping_cost contains the condition subtotal > 50. The trace for 50.00 asks whether 50.00 is greater than 50, produces false, follows the paid-shipping branch, and returns 5.99. This trace matches the observed failure. The requirement, however, says “$50 or more,” which means the comparison must include equality. Tests at $49.99 and $50.01 already pass, so the evidence isolates the defect to the boundary operator rather than the returned prices or the function call. A trace table can record the input, condition result, selected branch, and output. Isolating the first point where actual behavior differs from required behavior reduces the risk of unnecessary changes elsewhere.

A trace table follows a $50.00 subtotal through a false comparison to the paid branch and incorrect output.
A trace table follows a $50.00 subtotal through a false comparison to the paid branch and incorrect output.Source: Illustrated for this lesson

Comparing Competing Fixes

Competing fixes should be evaluated with the same tests and prioritized criteria. Fix A changes subtotal > 50 to subtotal >= 50. Fix B adds one cent before using the original comparison. Both may make the reported $50.00 test pass, but that single result is insufficient. Run the full suite, especially $49.99, $50.00, and invalid inputs. Fix A directly represents “$50 or more,” is easy to review, and does not alter the subtotal. Fix B hides the boundary rule inside arithmetic and may behave unpredictably with fractional cents or floating-point rounding. Compare correctness, clarity, reliability, compatibility, and customer impact. Fix A is better supported because its logic matches the requirement and creates fewer new risks. The argument should acknowledge that even Fix A does not solve separate issues involving currency representation or missing input validation.

A side-by-side comparison rates two proposed fixes against the requirement and the full test suite.
A side-by-side comparison rates two proposed fixes against the requirement and the full test suite.Source: Illustrated for this lesson

Documenting Evidence and Remaining Risks

A debugging record should connect the requirement, evidence, fix, and conclusion. State that orders of $50 or more require free shipping. Record that the original program failed the $50.00 boundary test by returning $5.99, while the revised comparison returned $0.00 and preserved expected results for $49.99, $50.01, $20.00, and $80.00. Explain why the inclusive comparison was selected over the arithmetic adjustment. Also report limitations: passing tests do not prove correctness for every possible input. Remaining risks may include floating-point currency errors, unclear treatment of coupons and taxes, negative values, and differences between software versions. Recommend using a decimal currency type, adding validation tests, and confirming whether the threshold applies before or after discounts. This evidence-based explanation acknowledges both the fix’s strengths and its unresolved weaknesses.

A debugging record links the shipping requirement and inclusive fix to passing tests, limitations, and recommendations.
A debugging record links the shipping requirement and inclusive fix to passing tests, limitations, and recommendations.Source: Illustrated for this lesson