Test assertions that document behavior
Assistance allowed only after prediction and observed evidence are recorded.
Prerequisites
Complete the previous activity; use the listed recovery link if blocked.
Learning Objective
Write a unittest.TestCase using assertEqual and assertRaises, run python -m unittest -v, and explain what the final OK line proves and does not prove.
Instructions
Each test_ method is one behavior claim; its name should read like documentation, for example test_empty_list_returns_zero. assertEqual(actual, expected) checks a value; with self.assertRaises(ExceptionType): proves that a call fails the way the contract says. There are two ways to run tests. python -m unittest -v in the project directory (the same as python -m unittest discover -v) discovers the modules named test_*.py in that directory. python test_counter.py runs only the tests defined in that one file, through the unittest.main() call at its bottom; unittest.main() does not discover sibling test_*.py modules.
Practice & Prediction Log
In an empty directory write counter.py containing count_non_empty(lines), which returns how many items contain non-whitespace text and raises TypeError when an item is not a str. In test_counter.py write a CounterTests class with four tests: an empty list returns 0; a whitespace-only line is ignored; mixed lines count only the non-empty ones; and a non-string item (for example None) raises TypeError, checked with assertRaises. Predict the four outcomes, then run python -m unittest -v in that directory. Expected run on Python 3.13/3.14 (tests run in alphabetical order; the timing varies; older Python versions may print a shorter name in the parentheses):
test_empty_list_returns_zero (test_counter.CounterTests.test_empty_list_returns_zero) ... ok
test_mixed_lines_count_only_non_empty (test_counter.CounterTests.test_mixed_lines_count_only_non_empty) ... ok
test_non_string_item_raises_type_error (test_counter.CounterTests.test_non_string_item_raises_type_error) ... ok
test_whitespace_only_line_is_ignored (test_counter.CounterTests.test_whitespace_only_line_is_ignored) ... ok
...
Ran 4 tests in 0.001s
OK
Predict which assertion executes inside each test. If the run reports that no tests ran (Ran 0 tests), check that your file is named test_*.py and that you ran discovery in the right directory.
Reference implementation (both files in the same directory; verified with Python 3.13 and 3.14):
# file: counter.py
def count_non_empty(lines):
"""Return how many items contain non-whitespace text.
Raise TypeError if an item is not a str.
"""
count = 0
for line in lines:
if not isinstance(line, str):
raise TypeError(f"expected str, got {type(line).__name__}")
if line.strip():
count += 1
return count
# end file
# file: test_counter.py
import unittest
from counter import count_non_empty
class CounterTests(unittest.TestCase):
def test_empty_list_returns_zero(self):
self.assertEqual(count_non_empty([]), 0)
def test_whitespace_only_line_is_ignored(self):
self.assertEqual(count_non_empty([" \t\n"]), 0)
def test_mixed_lines_count_only_non_empty(self):
self.assertEqual(count_non_empty(["alpha\n", " \n", "", "beta"]), 2)
def test_non_string_item_raises_type_error(self):
with self.assertRaises(TypeError):
count_non_empty(["alpha", None])
if __name__ == "__main__":
unittest.main()
# end file
Discovery run from that directory:
$ python -m unittest -v
test_empty_list_returns_zero (test_counter.CounterTests.test_empty_list_returns_zero) ... ok
test_mixed_lines_count_only_non_empty (test_counter.CounterTests.test_mixed_lines_count_only_non_empty) ... ok
test_non_string_item_raises_type_error (test_counter.CounterTests.test_non_string_item_raises_type_error) ... ok
test_whitespace_only_line_is_ignored (test_counter.CounterTests.test_whitespace_only_line_is_ignored) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Running the file directly executes only the tests in that file, via unittest.main(); the module shows up as __main__:
$ python test_counter.py -v
test_empty_list_returns_zero (__main__.CounterTests.test_empty_list_returns_zero) ... ok
test_mixed_lines_count_only_non_empty (__main__.CounterTests.test_mixed_lines_count_only_non_empty) ... ok
test_non_string_item_raises_type_error (__main__.CounterTests.test_non_string_item_raises_type_error) ... ok
test_whitespace_only_line_is_ignored (__main__.CounterTests.test_whitespace_only_line_is_ignored) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
What OK proves: every assertion held for these four inputs on this interpreter, including that None raised TypeError. What it does not prove: that count_non_empty is correct for inputs nobody tested, such as other non-str types or unusual whitespace characters.
The run ends with Ran 4 tests ... OK, including one assertRaises test, and you state that OK means every assertion held for these inputs — not that the function is correct for all possible inputs.
Authoritative Source Resources & Provenance
Python Standard Library: unittest
Relevant Section / Range: Classes and functions: Test cases (TestCase assert methods)
Verified Version & Date: Python 3.12 documentation; verified 2026-10-30
Normative reference for unittest.TestCase, assertEqual, assertRaises, and python -m unittest output.
unittest ships with the interpreter, so every example runs offline; help(unittest.TestCase) documents the assert methods.
Compared Candidate Sources & Selection Analysis (2)
Strong third-party runner, but it adds an installation step and its own conventions; unittest needs no environment change and is the baseline the rest of the curriculum assumes.
Docstring-embedded examples suit tiny pure functions; unittest was selected for the structured TestCase isolation the check suites need.