Why testing matters, pytest basics, and unit tests
The assert statements you've already been using
Every hidden test case in this entire course, all the way back to
Lesson 1, has been a plain assert statement checking a function’s
behavior against known inputs and expected outputs. That’s not a
simplification for teaching purposes — it’s genuinely what a test is
at its core. What’s been missing is a framework around those
assertions: a way to discover, run, and report on many of them at
once, consistently, instead of manually running a script and
eyeballing whether it crashed.
pytest is that framework — the standard tool for writing and running tests in Python.
Test discovery and a first test
pytest finds tests by convention, not configuration: a file named
test_*.py (or *_test.py), containing functions named test_*, is
automatically discovered and run — no registration step, no import
list to maintain.
# validators.py
def is_valid_tool_name(name) -> bool:
return isinstance(name, str) and len(name) > 0# test_validators.py
from validators import is_valid_tool_name
def test_valid_name_returns_true():
assert is_valid_tool_name("search") == True
def test_empty_string_returns_false():
assert is_valid_tool_name("") == False
def test_non_string_returns_false():
assert is_valid_tool_name(42) == FalseRunning pytest from the command line finds and runs every test_*
function in every test_*.py file it can locate:
click Run to see this pane's outputEach . represents one passing test. This is exactly the same
is_valid_tool_name function
from the OOP lesson’s decorators concept
— pytest isn’t asking you to write tests differently than the
assert-based checks you’ve already been reading all course; it’s
giving you a real tool to run them.
What a failing test actually shows you
def test_broken_example():
assert is_valid_tool_name("search") == False # deliberately wrongclick Run to see this pane's outputpytest shows exactly which assertion failed, what the actual value was
versus what was expected, and the specific line — considerably more
information than a bare AssertionError with no context would give
you on its own.
What makes this specifically a unit test
test_valid_name_returns_true and its neighbors are unit tests —
each one tests a single, small unit of code (here, one function) in
complete isolation: no file I/O, no network call, no database, nothing
external to the function itself. This matters because it’s what makes
unit tests fast (thousands can run in seconds) and deterministic (the
same input always produces the same result, with nothing external able
to make a run flaky). Not every test in this lesson will look like
this — testing a full FastAPI endpoint, covered next, involves
considerably more moving parts working together, which is a
meaningfully different kind of test, not just a bigger unit test.
What convention does pytest use to discover which functions are tests?