Testing with pytest
pytest is a framework for testing Python programs.
Until now, you tested a function by calling it under if __name__ == "__main__": and reading the output yourself.
pytest does the checking for you: you write down the answer you expect, and pytest tells you whether the function got it right.
This lab shows how to run pytest in the terminal and in VS Code, and how to write your own test functions.
Installing pytest
Open your CS149 folder in VS Code, and open a new terminal (Terminal > New Terminal).
Make sure the command line begins with (.venv), as in Step 5 of the VS Code setup.
Then run the following command:
This command installs pytest along with pytest-cov, which adds coverage.
You will use coverage at the end of this lab.
Example code
Create a Week08 folder in your CS149 folder.
Download the following files into your Week08 folder.
(Right click each link, and "Save link as…")
Open triangles.py and run the program.
You should see the following output:
Now open test_triangles.py.
Each test function calls a function from triangles.py and uses assert to state what the result should be.
If the result is what the assert says, nothing happens.
If it isn't, the test fails.
Running pytest in the Terminal
The terminal starts in your CS149 folder.
Like ruff, pytest looks at every file under the current folder, so first move into the Week08 folder:
Then run pytest:
This command runs every module in the current folder whose name begins with test_.
Within each module, every function whose name begins with test_ is called.
If any assertion fails, pytest displays a detailed error message.
Exercise 1
The example code currently passes all tests.
To see what failing a test looks like, introduce a bug in triangles.py.
For example, edit line 46 to say 0.0 * (a + b + c) instead of 0.5.
Save the file, and run pytest again.
Read the error message: which test failed, and what values did pytest compare?
Optional arguments
pytest works the same way as ruff. If you run pytest without any arguments, all test modules are run. Alternatively, you can run a specific test module:
You can run a specific test function using the -k option:
The -q (quiet) option makes the output less verbose:
Multiple options can be combined:
Exercise 2
Run each command listed above and see how the output changes.
The approx() function
Because floating-point arithmetic is imprecise, comparison with == can be unreliable.
For example, 0.1 + 0.2 == 0.3 is False, because 0.1 + 0.2 is 0.30000000000000004.
pytest includes a function named approx()
that checks whether a value is within a small difference of the expected value.
So 0.1 + 0.2 == approx(0.3) is True.
Notice that test_area() uses approx(), because the area() function returns a float.
Exercise 3
In the terminal, type python to start a Python shell.
Then run the following lines, one at a time:
exit() to return to the system shell when you're done.
Running pytest in VS Code
You can also run pytest without typing any commands!
- Click "Testing" (the beaker icon, near the bottom) on the Activity Bar (left sidebar).
- Expand the list until you see
test_triangles.pyand its three test functions. - Click the buttons to run pytest at any level: one test, one module, or everything.
- A green check means the test passed, and a red X means it failed.
- Click a failed test to see the same error message that you saw in the terminal.
- Open
test_triangles.pyand notice the buttons next to each test function.- You can also right-click a to "Debug Test" or "Run Test with Coverage".
The Testing view finds every test in your CS149 folder, not just the ones in Week08.
That's the same reason you cd Week08 before running pytest in the terminal.
Note
Over time, extra folders like __pycache__ and .pytest_cache will be created automatically.
These folders are hidden by the files.exclude setting in your .vscode/settings.json file.
Exercise 4
Run the tests in the Testing view, and confirm that test_area fails.
Then fix the bug you added on line 46 (change the 0.0 back to 0.5),
save the file, and run the tests again until every test passes.
Debugging a test
A failed test tells you that a function is wrong, but not where. The debugger lets you pause the program and run it one line at a time, watching the variables change.
- A breakpoint is a line where the program will pause. Click to the left of a line number to set one (a red dot appears), and click the dot again to remove it.
- When the program pauses, the Variables panel (on the left) shows the current value of every variable.
- The toolbar at the top of the window controls the program: Step Over (F10) runs the current line, Step Into (F11) goes inside a function call, and Continue (F5) runs until the next breakpoint.
Exercise 5
- Set a breakpoint on the first line of
area()intriangles.py. - Right-click the next to
test_areaintest_triangles.py. - Click "Debug Test" in the context menu.
- Step through the code with the debugger.
What are the values of
a,b,c, andsjust before the function returns?
Writing your own tests
Test functions use Python's assert statement.
- Ex:
assert a % 2 == 0, "value was odd, should be even" - The error message after the comma is optional, but helpful.
Follow this pattern when testing your own programs:
- For every module
m, create a module namedtest_m. - For every function
f, write a function namedtest_f. - Write enough
assertstatements to test every branch.
The provided tests also use raises().
area() and classify() don't return a value for an invalid triangle; they raise an error instead.
A with raises(ValueError): block passes only if the code inside it raises that error.
Exercise 6
The provided test functions are incomplete.
There are many possible errors that these tests wouldn't catch.
Add more assert statements to each function in test_triangles.py.
Each assert should use different triangle values and expected results.
Try to test every possible branch (return value) of each function.
To check your work, right-click the next to test_triangles.py and click "Run Test with Coverage".
Lines of triangles.py that your tests never ran are highlighted in red.
Keep adding tests until every line is covered.
Then submit your test_triangles.py to Gradescope.
Going further
See pytest's documentation and API Reference for additional details.