1. Reduce a failure before changing code
By the end
- Separate the intended contract from the current output.
- Choose a small counterexample that still exposes the fault.
A useful bug report is a repeatable experiment: exact input, expected behavior, observed behavior and a short way to run it. Start with the rule a person needs, not the formula already in the program. Here a batch holds at most capacity items; empty input needs no batch, and a partly filled final batch counts once.
The proposed formula items // capacity + 1 always adds a batch. For 9 items at capacity 4 it happens to give 3, but for 8 it gives 3 instead of 2. Reducing 8 to 4 preserves the exact-multiple mistake. Reducing again to 0 reveals the related empty-input mistake. Smaller inputs make the unnecessary extra batch easier to see.
Fix the cause and keep the counterexample. Integer ceiling division can use (items + capacity - 1) // capacity for this non-negative integer contract. Validate capacity before division. This lesson rejects booleans intentionally; a Boolean is not a quantity even though Python treats bool as an int subclass. Do not expand the input domain silently while debugging.
Worked example
Compare the broken and repaired count for 0, 1, 4, 5 and 8 items at capacity 4.
- Write the expected counts by packing on paper: 0, 1, 1, 2, 2.
- Trace integer division for 4: 4 // 4 is 1, so the extra +1 is the fault.
- Run both versions, retain checks for 0 and 4, then inspect neighboring 3 and 5 cases.
def batches(items, capacity):
if type(items) is not int or items < 0:
raise ValueError("items must be a non-negative integer")
if type(capacity) is not int or capacity <= 0:
raise ValueError("capacity must be a positive integer")
return (items + capacity - 1) // capacity
def broken_batches(items, capacity):
return items // capacity + 1
for items in (0, 1, 4, 5, 8):
print(items, broken_batches(items, 4), batches(items, 4))
assert batches(0, 4) == 0
assert batches(4, 4) == 1
The broken counts are [1, 1, 2, 2, 3]; the repaired counts are [0, 1, 1, 2, 2]. The initial formula can pass an ordinary case and still fail a boundary.
Try it yourself
Use capacity 3. Predict and test counts for 0, 2, 3, 4 and 6 items; identify the smallest positive input that exposes the original formula.
- Write expectations before running the code.
- Keep the input contract and record one smallest positive counterexample.
Reveal the practice solution
The counts are [0, 1, 1, 2, 2]. At 3 items the broken formula returns 2, although one batch fits them exactly. Inputs 1 and 2 do not expose this fault; 0 exposes the separate empty case.
def batches(items, capacity):
if type(items) is not int or items < 0:
raise ValueError("items must be a non-negative integer")
if type(capacity) is not int or capacity <= 0:
raise ValueError("capacity must be a positive integer")
return (items + capacity - 1) // capacity
observed = [batches(n, 3) for n in (0, 2, 3, 4, 6)]
assert observed == [0, 1, 1, 2, 2]
print(observed)
Watch for this mistake: A successful example is evidence for that input, not proof for every input. Do not change an independently justified expected answer to match broken output.
Sources and review date
- Python 3.12 — unittest
TestCase, per-method setUp, subTest, specific exception assertions and running suites. Exercises are original. Checked: .
Explanations, examples and quiz questions are original KitForma material. These links support technical facts and curriculum alignment.