← Back to course
Code Crew G6+ / Week 10 / Friday
5/6
Week 10 Β· Bugs and tests

Friday

A test file finds a bug
// Tracebacks, try and except, and assert
⏱ about 30 min

Friday: A test file finds a bug

Rocket has gathered his fleet math functions into a module called fleet_math.py. Raven suggests keeping the tests in a separate file, so the module stays tidy.

"The test file imports the module, like the trip planner did," Rocket says. "Then it checks every function."

Raven writes the test cases first, on paper, including an empty list of tanks. "What do you notice about that one?" she asks. "It is the case that broke the fuel report on Monday."

Nova hums. "Interesting," she says. "A test written for a bug you already met will catch it if it ever sneaks back in."

Rocket runs the tests, and one fails. He smiles. "Good. Now I know exactly what to fix."

The module and its tests

Here is the first version of the module. It has the same bug as Monday's fuel report:

# fleet_math.py
def average(numbers):
    """Average of a list of numbers."""
    return sum(numbers) / len(numbers)

def percent(part, whole):
    """part as a percentage of whole, rounded to 1 decimal place."""
    return round(part / whole * 100, 1)

The test file sits in the same folder and imports it:

# test_fleet_math.py
import fleet_math

def test_average():
    assert fleet_math.average([80, 60, 70]) == 70
    assert fleet_math.average([5]) == 5
    assert fleet_math.average([]) == 0

def test_percent():
    assert fleet_math.percent(1, 4) == 25.0
    assert fleet_math.percent(1, 3) == 33.3

test_average()
test_percent()
print("All tests passed")

Running test_fleet_math.py shows that the empty list test fails, inside fleet_math.py:

Traceback (most recent call last):
  File "test_fleet_math.py", line 13, in <module>
    test_average()
  File "test_fleet_math.py", line 7, in test_average
    assert fleet_math.average([]) == 0
  File "fleet_math.py", line 4, in average
    return sum(numbers) / len(numbers)
ZeroDivisionError: division by zero
Which test line (number) found the bug?
Which file must you change to fix it?

The fix

# fleet_math.py
def average(numbers):
    """Average of a list of numbers. An empty list gives 0."""
    if len(numbers) == 0:
        return 0
    return sum(numbers) / len(numbers)

def percent(part, whole):
    """part as a percentage of whole, rounded to 1 decimal place."""
    return round(part / whole * 100, 1)

Run test_fleet_math.py again, and every test passes:

All tests passed
WHY TESTS LIVE IN THEIR OWN FILE
  • Read the question.
  • Tap your answer.
What is the main benefit of a separate test file?
At your computer
1. Type the first fleet_math.py and save it in your mission folder.
2. Type test_fleet_math.py and save it in the same folder. Run it and read the traceback.
3. Fix average in fleet_math.py, save it, then run test_fleet_math.py again.
4. Add a test for percent(3, 4). Work out the answer by hand first.
PRACTICE: TESTS AND FIXES
  • Read the question.
  • Tap your answer.
A test fails inside fleet_math.py. Which file holds the fix?
What does percent(1, 4) return?
You add a new function to fleet_math.py. What should you add too?
After the fix, why run the whole test file again?
What does percent(3, 4) return?
What does average([10, 20]) return?
What does the fixed average([]) return?
StatementTrue or false?
A test file usually imports the module it tests.?
A test written for an old bug can catch it if it comes back.?
Once tests pass, you can delete them.?
The module and the test file must be in the same folder for import to work.?
WHY THIS EXERCISETests stay with the project, so every future change is checked against every old answer.
PUT THE TESTING WORKFLOW IN ORDER
  • ?Write the asserts in the test file
  • ?Fix the code in the module
  • ?Run all the tests again
  • ?Run the tests and read any failure
  • ?Work out the expected answers by hand
WHY THIS EXERCISEPlan, test, fix and test again is the loop professional teams use every day.

You found a bug with a test, fixed it, and proved the fix. That is how professional teams work. Next week you build Mission Log, and you will test it the same way.

← Thursday