Test Discovery and Running Tests
Test discovery rules (common pattern)
Section titled “Test discovery rules (common pattern)”Unittest can discover tests when:
- files match
test*.py - tests are under a folder like
tests/ - test methods start with
test_
Typical project layout
Section titled “Typical project layout”project/
src/
app.py
tests/
test_app.pyRunning discovery
Section titled “Running discovery”From the project root:
python -m unittest discoverTo specify a tests folder:
python -m unittest discover -s testsRun a specific test module
Section titled “Run a specific test module”python -m unittest tests.test_appIf discovery finds 0 tests, check:
- filenames
- class inherits from
unittest.TestCase - methods start with
test_
🧪 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”What discovery looks for
Section titled “What discovery looks for”python -m unittest discover # from the current directory
python -m unittest discover -s tests -t . # start in tests/, resolve imports from .
python -m unittest discover -p "check_*.py" # a different file pattern
python -m unittest discover -v # name each test as it runs flowchart TD
D["discover -s tests -t ."] --> W["walk tests/ recursively"]
W --> P{"filename matches test*.py?"}
P -->|"no"| SK["ignored"]
P -->|"yes"| I["import it"]
I --> C{"TestCase subclasses?"}
C -->|"yes"| M{"methods starting with test?"}
M -->|"yes"| R["run them, alphabetically"]
M -->|"no"| SK
Four things must all be true for a test to run, and each is a silent failure on its own:
- the file matches
test*.py - the class subclasses
unittest.TestCase - the method name starts with
test - the package is importable from the top-level directory
-s and -t are different, and that is the usual problem
Section titled “-s and -t are different, and that is the usual problem”-sis where to start looking for test files.-tis the top-level directory, which is what gets put onsys.path.
Running discover -s tests alone makes tests/ the top level, so from calc import add
fails — calc.py lives one level up. Measured working invocation:
python -m unittest discover -s tests -t .with an __init__.py inside tests/ so it is a package.
Reading the result line
Section titled “Reading the result line”Measured on a suite with eight tests:
Ran 8 tests in 0.004s
FAILED (skipped=2, expected failures=1, unexpected successes=1)Every counter there matters, and the run failed without a single ordinary failure — because of the unexpected success, which the next page covers.
See it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
What is the difference between -s and -t in unittest discover?
Running discover -s tests alone makes tests/ the top level, so importing a module one directory up fails. The working form measured here was -s tests -t .
pch.quizShowAnswer
B — -s is where to start looking for test files; -t is the top-level directory placed on sys.path — Running discover -s tests alone makes tests/ the top level, so importing a module one directory up fails. The working form measured here was -s tests -t .
-
Discovery matches no files and the suite prints Ran 0 tests. What is the exit status?
A green CI run can mean every test passed or that discovery matched nothing. Assert the count if the distinction matters.
pch.quizShowAnswer
B — zero — success, indistinguishable from all tests passing — A green CI run can mean every test passed or that discovery matched nothing. Assert the count if the distinction matters.
-
A test method is defined but never runs. Which of these would cause that silently?
All four discovery conditions fail silently. That is why a suite can grow while its effective coverage quietly shrinks.
pch.quizShowAnswer
D — any of the above — All four discovery conditions fail silently. That is why a suite can grow while its effective coverage quietly shrinks.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading