Skip to content

Writing Your First Test Case

A clean test follows Arrange → Act → Assert:

  1. Arrange: set up inputs
  2. Act: call the code under test
  3. Assert: verify output/side effects
calculator.py
def divide(a: float, b: float) -> float:
    return a / b
test_calculator.py
import 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()

Good test names describe behavior:

  • test_divide_by_two_returns_half
  • test_divide_by_zero_raises

Avoid vague names like test1.

Every test has the same three parts, and naming them keeps a test readable when it fails six months later:

diagram Diagram mermaid
tests/test_calc.py
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)  # Assert

Three rules the framework enforces, and one convention:

  • The class must subclass unittest.TestCase.
  • Test methods must start with test — a method named check_add is never run, and nothing warns you.
  • Assertions come from self, not the assert statement (the next page shows why).
  • The file is conventionally named test_*.py so discovery finds it.
fixtures.py
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 failed

Measured: 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.

commands
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 runs
sketch The order things actually run in p5.js
setUpClass runs once, setUp runs before every test, and the tests themselves run in alphabetical order rather than the order written.
pch.quizTag pch.quizDefaultTitle
  1. You write a method named check_addition inside a TestCase. What happens when you run the suite?

    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.

  2. In what order do test methods within a TestCase run?

    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.

  3. How often does setUpClass run compared with setUp?

    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.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading