Pytest¶
Test files are located in the tests
directory and have either the test_
prefix or _test
suffix. Within the test file, all test functions should start with test_
.
Example Run¶
The simplest type of test is an assertion. This is where we assert that something is true. To see this in action, lets create a new test file called test_simple.py
. Paste in the following code and save the file:
Simple Test
Here we have a simple function func()
which takes an integer and adds one to it. The test is the function below, test_answer()
, which inputs the value three into func()
and expects an output of four. When we start the test by running pytest
in the same directory, we get the following output:
============================= test session starts ==============================
platform darwin -- Python 3.9.12, pytest-7.1.2, pluggy-1.0.0
rootdir: /Users/name/dir
collected 1 item
test_sample.py . [100%]
============================== 1 passed in 0.03s ===============================
You can see that the function works as intended and the test passes. Now if we change line 6 to falsely assert that it should equal 5, this is the output that we receive when a test fails.
============================= test session starts ==============================
platform darwin -- Python 3.9.12, pytest-7.1.2, pluggy-1.0.0
rootdir: /Users/name/dir
collected 1 item
test_sample.py F [100%]
=================================== FAILURES ===================================
_________________________________ test_answer __________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:6: AssertionError
=========================== short test summary info ============================
FAILED test_sample.py::test_answer - assert 4 == 5
============================== 1 failed in 0.10s ===============================
In this output, pytest
tells exactly where the error occurred, and it shows in the summary that the test failed because we are trying to assert that four equals five.
Multiple Tests in a File¶
Using pytest
, it is also possible to place multiple tests in a single file. May also want to group them in a class, as done below:
Grouping Tests in a Class
Danger
Be aware though that each test has a unique instance of the class. Try to foresee what will happen in the example test below, then run the test.
Test Fixtures¶
Test functions can request fixtures they require by declaring them as arguments. When pytest runs a test, it looks at the parameters in that test function, and searches for fixtures that have the same names as those parameters. Once pytest finds them, it runs those fixtures, captures what they returned (if anything), and passes those objects into the test function as arguments.
Essentially, the pytest fixture system gives the ability to define a generic setup step that can be reused over and over, just like a normal function would be used. Two different tests can request the same fixture and have pytest give each test their own result from that fixture.
This is extremely useful for making sure tests aren’t affected by each other. We can use this system to make sure each test gets its own fresh batch of data and is starting from a clean state so it can provide consistent, repeatable results.
Fixture Example
import pytest
class Fruit:
def __init__(self, name):
self.name = name
self.cubed = False
def cube(self):
self.cubed = True
class FruitSalad:
def __init__(self, *fruit_bowl):
self.fruit = fruit_bowl
self._cube_fruit()
def _cube_fruit(self):
for fruit in self.fruit:
fruit.cube()
# Arrange
@pytest.fixture
def fruit_bowl():
return [Fruit("apple"), Fruit("banana")]
def test_fruit_salad(fruit_bowl):
# Act
fruit_salad = FruitSalad(*fruit_bowl)
# Assert
assert all(fruit.cubed for fruit in fruit_salad.fruit)
Parameterising of Test Arguments¶
A built in decorator, pytest.mark.parameterize
, enables the parameterisation of arguments for a test function. This allows a compact way of testing various argument values in a test.
Parameterisation Example
Note that it is also possible to combine multiple parameterised arguments by stacking the parameterized
decorators in order to work through multiple permutations. The example below will set the values of (x,y) in the following order: (0,2), (1,2), (0,3), and (1,3).