Writing Your First Test Case
The AAA pattern
Section titled “The AAA pattern”A clean test follows Arrange → Act → Assert:
- Arrange: set up inputs
- Act: call the code under test
- Assert: verify output/side effects
Example: function + test
Section titled “Example: function + test”def divide(a: float, b: float) -> float:
return a / bimport unittest
from calculator import divide
class TestCalculator(unittest.TestCase):
def test_divide_returns_quotient(self):
# Arrange
a, b = 10, 2
# Act
result = divide(a, b)
# Assert
self.assertEqual(result, 5)
if __name__ == "__main__":
unittest.main()Test naming tips
Section titled “Test naming tips”Good test names describe behavior:
test_divide_by_two_returns_halftest_divide_by_zero_raises
Avoid vague names like test1.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Write a unittest TestCase
Section titled “Exercise 1 – Write a unittest TestCase”Exercise 2 – assertRaises
Section titled “Exercise 2 – assertRaises”Exercise 3 – setUp and tearDown
Section titled “Exercise 3 – setUp and tearDown”Arrange, Act, Assert
Section titled “Arrange, Act, Assert”Every test has the same three parts, and naming them keeps a test readable when it fails six months later:
flowchart LR A["Arrange
build the inputs"] --> B["Act
call the thing under test"] B --> C["Assert
compare against what you expect"] C --> P["pass: nothing printed"] C --> F["fail: the assertion says what differed"]
import unittest
from calc import add
class TestAdd(unittest.TestCase):
def test_add(self):
result = add(2, 3) # Arrange and Act
self.assertEqual(result, 5) # AssertThree rules the framework enforces, and one convention:
- The class must subclass
unittest.TestCase. - Test methods must start with
test— a method namedcheck_addis never run, and nothing warns you. - Assertions come from
self, not theassertstatement (the next page shows why). - The file is conventionally named
test_*.pyso discovery finds it.
setUp, tearDown, and how often they run
Section titled “setUp, tearDown, and how often they run”class TestAdd(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("runs ONCE for the whole class")
def setUp(self):
self.log = ["setUp"] # runs before EVERY test method
def tearDown(self):
pass # runs after every test method, even if it failedMeasured: setUpClass printed once across eight tests. setUp runs per test, which is
what gives each test a clean object to work with — and why one test cannot corrupt
another’s state.
Running it
Section titled “Running it”python -m unittest tests.test_calc # one module
python -m unittest discover -s tests -t . # find everything
python -m unittest discover -v # name each test as it runsSee it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
You write a method named check_addition inside a TestCase. What happens when you run the suite?
unittest collects methods whose names start with test. A misnamed method is silently ignored, which is why a suite can report all-passing while covering nothing.
pch.quizShowAnswer
B — it is never run, and nothing warns you — unittest collects methods whose names start with test. A misnamed method is silently ignored, which is why a suite can report all-passing while covering nothing.
-
In what order do test methods within a TestCase run?
Measured: a class defining test_add first and test_unexpected_pass last ran them alphabetically. A test that depends on another running first will break on a rename.
pch.quizShowAnswer
B — alphabetically by method name — Measured: a class defining test_add first and test_unexpected_pass last ran them alphabetically. A test that depends on another running first will break on a rename.
-
How often does setUpClass run compared with setUp?
Measured setUpClass printing once across eight tests. Per-test setUp is what gives each test a clean starting state.
pch.quizShowAnswer
B — setUpClass runs once per class; setUp runs before every test method — Measured setUpClass printing once across eight tests. Per-test setUp is what gives each test a clean starting state.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading