Common Patterns for Tests¶
In this section you will learn some useful features of pytest that can make your tests succinct and easy to maintain.
Parametrized Tests¶
Tests that apply the same general test logic to a collection of different parameters can use parametrized tests. For example, this:
import numpy as np
from ..refraction import snell
def test_perpendicular():
# For any indexes, a ray normal to the surface should not bend.
# We'll try a couple different combinations of indexes....
actual = snell(0, 2.00, 3.00)
expected = 0
assert actual == expected
actual = snell(0, 3.00, 2.00)
expected = 0
assert actual == expected
can be rewritten as:
import numpy as np
import pytest
from ..refraction import snell
@pytest.mark.parametrize('n1, n2',
[(2.00, 3.00),
(3.00, 2.00),
])
def test_perpendicular(n1, n2):
# For any indexes, a ray normal to the surface should not bend.
# We'll try a couple different combinations of indexes....
actual = snell(0, n1, n2)
expected = 0
assert actual == expected
The string 'n1, n2'
specifies which parameters this decorator will fill in.
Pytest will run test_perpendicular
twice, one for each entry in the
list [(2.00, 3.00), (3.00, 2.00)]
, passing in the respective values n1
and n2
as arguments.
From here we refer you to the pytest parametrize documentation.
Fixtures¶
Tests that have different logic but share the same setup code can use pytest fixtures. For example, this:
import numpy as np
def test_height():
# Construct a 1-dimensional Gaussian peak.
x = np.linspace(-10, 10, num=21)
sigma = 3.0
peak = np.exp(-(x / sigma)**2 / 2) / (sigma * np.sqrt(2 * np.pi))
expected = 1 / (sigma * np.sqrt(2 * np.pi))
# Test that the peak height is correct.
actual = np.max(peak)
assert np.allclose(actual, expected)
def test_nonnegative():
# Construct a 1-dimensional Gaussian peak.
x = np.linspace(-10, 10, num=20)
sigma = 3.0
peak = np.exp(-(x / sigma)**2 / 2) / (sigma * np.sqrt(2 * np.pi))
# Test that there are no negative values.
assert np.all(peak >= 0)
can be written as:
import pytest
import numpy as np
@pytest.fixture
def peak():
# Construct a 1-dimensional Gaussian peak.
x = np.linspace(-10, 10, num=21)
sigma = 3.0
peak = np.exp(-(x / sigma)**2 / 2) / (sigma * np.sqrt(2 * np.pi))
return peak
def test_height(peak):
expected = 1 / (sigma * np.sqrt(2 * np.pi))
# Test that the peak height is correct.
actual = np.max(peak)
assert np.allclose(actual, expected)
def test_nonnegative(peak):
# Test that there are no negative values.
assert np.all(peak >= 0)
To reuse a fixture in multiple files, add it to conftest.py
located in the
tests/
directory. It will automatically be imported by pytest into each
test module.
From here we refer you to the pytest fixtures documentation.
Skipping Tests¶
Sometimes it is useful to skip specific tests under certain conditions. Examples:
import pytest
import sys
@pytest.mark.skipif(sys.version_info < (3, 7),
reason="requires python3.7 or higher")
def test_something():
...
@pytest.mark.skipif(sys.platform == 'win32',
reason="does not run on windows")
def test_something_that_does_not_work_on_windows():
...
def test_something_that_needs_a_special_dependency():
some_library = pytest.importorskip("some_library")
...
From here we refer you to the pytest skipping documentation.