test suite
In Python, a test suite is a collection of test cases that a test runner loads and executes together as a single unit. The unittest module models the idea with its TestSuite class, which aggregates individual test cases and even other suites into one object that a test runner can execute.
You’ll rarely build that object by hand. Running python -m unittest discover walks your project for files matching test*.py, loads every test case it finds, and collects them into a suite for you. pytest does the same for files named test_*.py or *_test.py. Whatever the runner collects becomes your suite for that run.
Grouping tests this way gives you one verdict for the whole project. A single command runs everything, and the suite passes only when every test in it passes. That’s the check a continuous integration pipeline runs on each commit before it allows a merge.
Example
Say you’re testing a shopping cart and you want the checks on totals and the checks on discounts to run together. Both TestCase classes can live in the same file:
test_cart.py
import unittest
def total(prices):
return round(sum(prices), 2)
def apply_discount(amount, percent):
return round(amount * (1 - percent / 100), 2)
class TestTotals(unittest.TestCase):
def test_empty_cart_is_free(self):
self.assertEqual(total([]), 0)
def test_sums_prices(self):
self.assertEqual(total([9.99, 5.01]), 15.0)
class TestDiscounts(unittest.TestCase):
def test_applies_percent(self):
self.assertEqual(apply_discount(15.0, 10), 13.5)
if __name__ == "__main__":
unittest.main()
Running the file with the -v flag reports every test that the suite picked up:
$ python test_cart.py -v
test_applies_percent (__main__.TestDiscounts.test_applies_percent) ... ok
test_empty_cart_is_free (__main__.TestTotals.test_empty_cart_is_free) ... ok
test_sums_prices (__main__.TestTotals.test_sums_prices) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
The unittest.main() call gathers the tests from both TestCase classes into one suite, so one command covers all three. The runner reports them as one block and prints OK only because none of them failed. Add a second test file, and python -m unittest discover sweeps it into the same suite. You still run one command, and you still get one verdict.
The project below holds five tests spread across three test files, plus a cart.py module that neither runner collects. Switch runners to see which files each one collects into the suite, then click any test to break it and watch the run’s single verdict change:
Related Resources
Tutorial
Python's unittest: Writing Unit Tests for Your Code
In this tutorial, you'll learn how to use the unittest framework to create unit tests for your Python code. Along the way, you'll also learn how to create test cases, fixtures, test suites, and more.
For additional information on related topics, take a look at the following resources:
- pytest Tutorial: Effective Python Testing (Tutorial)
- Test-Driven Development With pytest (Course)
- Continuous Integration With Python: An Introduction (Tutorial)
- Continuous Integration and Deployment for Python With GitHub Actions (Tutorial)
- Continuous Integration With Python (Course)
- Testing Your Code With Python's unittest (Course)
- Python's unittest: Writing Unit Tests for Your Code (Quiz)
- Testing Your Code With pytest (Course)
- Effective Testing with Pytest (Quiz)
- Python Continuous Integration and Deployment Using GitHub Actions (Course)
- GitHub Actions for Python (Quiz)
By Martin Breuss • Updated Aug. 24, 2026