A unit test calls one function with a known input and checks that the result is what you expect. This program is a tiny test runner. It runs a list of test cases, reports each one, and finds a real bug.
The program
def run_tests(func, cases): passed = 0 for args, expected in cases: call = f"{func.__name__}({', '.join(repr(a) for a in args)})" try: result = func(*args) except Exception as error: result = f"{type(error).__name__}: {error}" if result == expected: passed += 1 print(f"PASS {call} == {expected!r}") else: print(f"FAIL {call}: expected {expected!r}, got {result!r}") failed = len(cases) - passed print(f"\n{passed} passed, {failed} failed") return failed == 0 def is_leap_year(year): # bug on purpose: this forgets the rule for years divisible by 400 return year % 4 == 0 and year % 100 != 0 cases = [ ((2024,), True), ((2023,), False), ((1900,), False), ((2000,), True), ] run_tests(is_leap_year, cases)
PASS is_leap_year(2024) == True PASS is_leap_year(2023) == False PASS is_leap_year(1900) == False FAIL is_leap_year(2000): expected True, got False 3 passed, 1 failed
How it works
- Each test case is a pair: a tuple of arguments and the expected result.
func(*args)unpacks the tuple into the function's arguments, so the runner works for functions with any number of arguments. - The
try/exceptcatches an error inside the function, so one crashing test does not stop the others. The error is shown as the result, so it appears in the FAIL line. - The runner returns
Trueonly if every test passed. A script could use that to stop before a release. - The test for 2000 fails. 2000 divides by 400, so it is a leap year, but the function forgot that rule. Tests with the unusual cases (1900 and 2000) are the ones that find bugs like this.
Try changing it
Fix the function with the full rule and run the tests again:
def is_leap_year(year): return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0
All 4 tests now pass. For real projects, use Python's built-in unittest module or the popular pytest package. They find test functions for you, show clear reports, and work the same way: call the code and compare the result with what you expected.