One failure should not hide the rest
Plain asserts have a flaw: the program stops at the first failure, so you never learn whether the other tests pass. Real test tools like pytest and unittest solve this by running each test separately and printing a report.
We can build the heart of such a test runner in ten lines. The idea:
- write each test as a small function whose name starts with
test_ - loop over the test functions
- call each one inside
try/except AssertionError(exception handling from Advanced Python) - print
PASSorFAILwith the test's name, then a summary
Building it once demystifies every test framework you will ever meet. They all do this, plus conveniences.
Code exercise · python
Run the mini runner. Note test.__name__, a function attribute that holds the function's own name, which gives us readable output for free.
Quiz
Why does the runner call each test inside try/except AssertionError?
Code exercise · python
Your turn: the multiply function below has a bug, and the tests catch it. Run first to see the FAIL lines, then fix multiply so the report shows 3/3 passed.
Code exercise · python
One upgrade before moving on. Buggy code does not always fail an assert politely — it can crash with any exception, like the ZeroDivisionError here. Run this: the runner only catches AssertionError, so the whole run dies and the summary never prints. Add a second except clause, except Exception as e, that prints FAIL <name> (crashed: <type name>) using type(e).__name__, so the runner survives any crash. Real runners like pytest do exactly this.
Problem
In the runner above, one test fails with AssertionError but the loop keeps going and prints a summary at the end. What is the exact name of the exception type that a failing assert raises?