Chapter 17: Testing — Solutions
Worked solutions to the 15 Find the Bug exercises in Chapter 17: Testing. Each one names the kind of bug, explains why the original misbehaved, and shows the corrected program.
Solutions stay folded until you open them — try the exercise first; the diagnosis is where the learning is.
Solutions 17.1.1–17.1.5
Solution 17.1.1 — Rectangle area check
Show the diagnosis and the fix
Bug type: Logical
The function adds length and width instead of multiplying them, so it returns 7 and the assert fails. Area of a rectangle is length times width.
def rectangle_area(length, width):
return length * width
assert rectangle_area(4, 3) == 12
print("passed")
Solution 17.1.2 — Celsius to Fahrenheit check
Show the diagnosis and the fix
Bug type: Logical
The function is correct, but the expected value in the test is wrong: 100 °C is 212 °F, not 211. The fix corrects the expected value.
def c_to_f(celsius):
return celsius * 9 / 5 + 32
assert c_to_f(100) == 212
print("passed")
Solution 17.1.3 — Average of three grades
Show the diagnosis and the fix
Bug type: Logical
Operator precedence divides only c by 3 before adding, instead of dividing the whole sum. Parentheses around the sum fix the formula so the average is computed correctly.
def average(a, b, c):
return (a + b + c) / 3
assert average(80, 90, 100) == 90
print("passed")
Solution 17.1.4 — Percent of a total
Show the diagnosis and the fix
Bug type: Syntax
The function header is missing the colon at the end of the def line, so the file will not parse. Adding the colon fixes it.
def percent(part, whole):
return part / whole * 100
assert percent(25, 50) == 50
print("passed")
Solution 17.1.5 — Total cost with tax
Show the diagnosis and the fix
Bug type: Logical
The function is correct (20 + 2 = 22), but the expected value in the assert is 20 instead of 22, so the check fails. The fix corrects the expected value.
def total_with_tax(price):
return price + price * 0.10
assert total_with_tax(20) == 22
print("passed")
Solutions 17.2.1–17.2.5
Solution 17.2.1 — Perimeter test
Show the diagnosis and the fix
Bug type: Logical
The function correctly returns 4 × 5 = 20, but the test asserts 25, so it fails. The fix corrects the expected value to 20.
def square_perimeter(side):
return 4 * side
def test_square_perimeter():
assert square_perimeter(5) == 20
Solution 17.2.2 — Speed test
Show the diagnosis and the fix
Bug type: Logical
Speed is distance divided by time, but the function multiplies them, returning 200 instead of 50. Changing * to / fixes the formula.
def speed(distance, time):
return distance / time
def test_speed():
assert speed(100, 2) == 50
Solution 17.2.3 — Discount test
Show the diagnosis and the fix
Bug type: Logical
A pytest test should check with assert and not return a value; the trailing return True makes the test look like it passes regardless. Removing it leaves the assertion as the real check.
def discounted_price(price, percent_off):
return price - price * percent_off / 100
def test_discounted_price():
assert discounted_price(50, 20) == 40
Solution 17.2.4 — Counting even numbers
Show the diagnosis and the fix
Bug type: Logical
pytest only discovers functions whose names start with test_; check_count_evens will never be collected or run. Renaming it to test_count_evens lets pytest find it.
def count_evens(numbers):
return len([n for n in numbers if n % 2 == 0])
def test_count_evens():
assert count_evens([1, 2, 3, 4, 6]) == 3
Solution 17.2.5 — Doubling a recipe
Show the diagnosis and the fix
Bug type: Syntax
The assert uses a single = (assignment) instead of == (comparison), which is a syntax error. Using == fixes the comparison.
def double_recipe(cups):
return cups * 2
def test_double_recipe():
assert double_recipe(3) == 6
Solutions 17.3.1–17.3.5
Solution 17.3.1 — Sum of an empty list
Show the diagnosis and the fix
Bug type: Logical
The function correctly returns 0 for an empty list, but the test asserts 1, so the edge-case check fails. The fix corrects the expected value to 0.
def total(numbers):
running = 0
for n in numbers:
running += n
return running
def test_total_empty():
assert total([]) == 0
Solution 17.3.2 — Floating-point area
Show the diagnosis and the fix
Bug type: Logical
Comparing a floating-point result with plain == fails here because area_circle(2) is 12.566370…, not exactly 12.566. Wrap the expected value in pytest.approx to allow a tiny tolerance.
import math
import pytest
def area_circle(radius):
return math.pi * radius ** 2
def test_area_circle():
assert area_circle(2) == pytest.approx(12.566, abs=1e-3)
Solution 17.3.3 — Absolute value of negatives
Show the diagnosis and the fix
Bug type: Logical
For a negative input the function returns the number unchanged instead of negating it, so abs_value(-7) gives -7. Returning -number in the negative branch fixes the edge case.
def abs_value(number):
if number < 0:
return -number
return number
def test_abs_value_negative():
assert abs_value(-7) == 7
Solution 17.3.4 — Parametrized squares
Show the diagnosis and the fix
Bug type: Logical
The third parameter case expects 20, but 5² = 25, so that case fails. Correcting the expected value to 25 makes all parametrized cases pass.
import pytest
def square(n):
return n ** 2
@pytest.mark.parametrize("n, expected", [
(0, 0),
(2, 4),
(5, 25),
])
def test_square(n, expected):
assert square(n) == expected
Solution 17.3.5 — Approximate division
Show the diagnosis and the fix
Bug type: Logical
Setting abs=0 gives pytest.approx zero tolerance, so 0.3333… never matches 0.3333 and the test fails. Using a small tolerance (or the default) lets the approximate comparison succeed.
import pytest
def divide(a, b):
return a / b
def test_divide():
assert divide(1, 3) == pytest.approx(0.3333, abs=1e-4)