Organizing Tests into Test Suites
Why test suites?
Section titled “Why test suites?”Suites let you:
- group tests by feature
- run a subset for smoke/regression
- create custom runners
Example suite
Section titled “Example suite”import unittest
class TestA(unittest.TestCase):
def test_one(self):
self.assertTrue(True)
class TestB(unittest.TestCase):
def test_two(self):
self.assertEqual(1 + 1, 2)
def load_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA))
suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB))
return suite
if __name__ == "__main__":
runner = unittest.TextTestRunner(verbosity=2)
runner.run(load_suite())In most modern codebases, you’ll rely on discovery.
Suites are most useful for curated runs.
A TestSuite is a container you build yourself
Section titled “A TestSuite is a container you build yourself”Discovery is the usual entry point. A TestSuite is what you reach for when you need to
choose and order the tests explicitly:
import unittest
from tests.test_calc import TestAdd
from tests.test_msgs import TestMessages
def build_suite():
suite = unittest.TestSuite()
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromTestCase(TestAdd))
suite.addTest(TestMessages("test_in")) # one specific method
return suite
if __name__ == "__main__":
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(build_suite())
raise SystemExit(0 if result.wasSuccessful() else 1) flowchart TD
L["TestLoader"] --> A["loadTestsFromTestCase(Class)"]
L --> B["loadTestsFromModule(module)"]
L --> C["loadTestsFromName('pkg.mod.Class.method')"]
A --> S["TestSuite"]
B --> S
C --> S
S --> N["suites can contain suites"]
S --> R["TextTestRunner(verbosity=2).run(suite)"]
R --> W["result.wasSuccessful() -> your exit code"]
raise SystemExit(...) on the last line is the part people leave out. Without it, a custom
runner script exits 0 whatever happened, and CI reports success on a failing suite.
Grouping by speed rather than by module
Section titled “Grouping by speed rather than by module”The useful grouping is usually not “all the model tests” but “the tests I can afford to run on every save”:
def fast_suite():
loader = unittest.TestLoader()
suite = unittest.TestSuite()
for cls in (TestAdd, TestMessages):
for name in loader.getTestCaseNames(cls):
if not name.endswith("_slow"):
suite.addTest(cls(name))
return suiteThis is exactly what pytest markers do declaratively, which is one of the reasons the next phase exists.
Sharing expensive setup across a whole suite
Section titled “Sharing expensive setup across a whole suite”def setUpModule():
global _server
_server = start_test_server() # once for the entire module
def tearDownModule():
_server.stop()| hook | runs |
|---|---|
setUpModule | once per module |
setUpClass | once per class |
setUp | before every test method |
Push shared setup as far up as it will go only when it is genuinely read-only. A
database connection shared across a class is fine; a mutable object shared across tests
reintroduces exactly the coupling setUp exists to prevent.
See it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
You write a script that builds a TestSuite and runs it with TextTestRunner. What must the script do that discovery does for you?
A custom runner script exits 0 by default whatever the result. raise SystemExit(0 if result.wasSuccessful() else 1) is the missing line.
pch.quizShowAnswer
B — set the exit code from result.wasSuccessful(), or CI reports success on a failing suite — A custom runner script exits 0 by default whatever the result. raise SystemExit(0 if result.wasSuccessful() else 1) is the missing line.
-
Which hook runs once per module?
setUpModule runs once per module, setUpClass once per class, and setUp before every test method. Push shared setup upward only when it is read-only.
pch.quizShowAnswer
C — setUpModule — setUpModule runs once per module, setUpClass once per class, and setUp before every test method. Push shared setup upward only when it is read-only.
-
A test passes in the suite but fails when run alone. What does that indicate?
Since tests run alphabetically, shared mutable state creates order-dependence that survives until someone renames a method.
pch.quizShowAnswer
B — the test depends on state left behind by another test — Since tests run alphabetically, shared mutable state creates order-dependence that survives until someone renames a method.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading