Pytest has rapidly established itself as the standard python testing framework. Its power lies in its simplicity, which makes it super easy to write tests and to run them. Tests can be as simple as the ones that we've already seen, so it is perfect for small projects. However, hidden behind the simple exterior pytest
provides a great deal of power and flexibility, meaning that it scales well to large and complex projects.
In this section we will learn how to automatically find and run tests using pytest
, then query and interpret the output.
Pytest comes with a command-line tool called, unsurprisingly, pytest
. (With older versions of python this was called py.test
.) When run, pytest
searches for tests in all directories and files below the current directory, collects the tests together, then runs them. Pytest uses name matching to locate the tests. Valid names start or end with test
, e.g.
# Files:
test_file.py file_test.py
# Functions:
def test_func(): def func_test():
In what follows, we will use the convention of test_*
when naming files and functions.
You can specify one or more paths and pytest
will only look for tests in those paths, e.g.
pytest /path/to/my/awesome/module
You can also specify the name of a file (or files) containing test functions, e.g.
pytest /path/to/my/awesome/module/test/tests.py
When writing a python package it is good practice to set up a directory structure in order to keep things tidy. Throughout this course we will use the following:
mypkg/
__init__.py
whizz.py
bang.py
test/
__init__.py
test_whizz.py
test_bang.py
Here the __init__.py
files makes python aware that the directory should be treated as a package (a collection of python modules). Assuming we're in the top level directory (where our notebooks reside) this allows us to do the following:
import mypkg
Let's dive in and run some tests using mypkg
that was introduced in the previous section.
!pytest mypkg
What just happened?
Well, pytest
searched within the mypkg
directory and collected a total of 17 tests. These tests were spread accross two files:
mypkg/test/test_errors.py
mypkg/test/test_mymath.py
The tests were then run and a single failure was reported for the function test_greaterThan
. This test failed when asserting that 3 shouldn't be greater than 7.
E assert not True
E + where True = greaterThan(3, 7)
What are all the cryptic symbols next to the name of each test file?
mypkg/test/test_mymath.py x.......s...F.... [100%]
To get more detailed information about each test, we can run pytest
using the verbose flag.
!pytest mypkg -v
We now have more detailed information about each test. Matching up the output with the symbols we can see that:
. = PASSED
s = SKIPPED
F = FAILED
x = xfail
What does xfail
mean, and why were some tests skipped? Also, note that some tests were run mutliple times, e.g. test_sub
appears 3 times.
What do the numbers in square brackets mean? Looking at the report for the test that failed, we can see that the square brackets show the arguments to the test functions, e.g. x=3
and y=7
.
To report more information on tests that were recorded as SKIPPED
, or xfail
, we can run pytest
as follows.
!pytest mypkg -rsx
We now have additional information about these tests. One test was skipped because it is only valid on the Windows operating system, another because it was testing functionality that hasn't been implemented yet.
By now, you might have guessed that xfail
means expected to fail. You can see that the test_add
was expected to fail with the reason "Broken code. Working on a fix." This was the function that we found to be broken in the previous section.
Let's switch to the terminal and try to work out why the test was failing. Since we know that test_greaterThan
was the culprit, let's look at the greaterThan
function in mypkg/mymodule.py
to see what could be going wrong.
Well, that was silly. Having fixed the bug, let's confirm that our tests now pass. Let's also run pytest
in quiet mode. Here we only get a minimal output showing the progress of the tests and reports for any failures (so hopefully none!).
!pytest mypkg -q