Parametrizing tests is great, but it's not a solution for everything. Suppose
now that we'd like to add another test for normalize
. Does the script below
really make sense?
import pytest
import numpy as np
def normalize(X):
return (X - X.min())/(X.max() - X.min())
@pytest.mark.parametrize("x", [1, 2, 30], ids=lambda d: f"x={d}")
@pytest.mark.parametrize("y", [1, 2, 30], ids=lambda d: f"y={d}")
def test_shape_same(x, y):
if x == y:
pytest.skip("Not of interest")
X = np.random.normal((x, y))
X_norm = normalize(X)
assert X.shape == X_norm.shape
@pytest.mark.parametrize("x", [1, 2, 30], ids=lambda d: f"x={d}")
@pytest.mark.parametrize("y", [1, 2, 30], ids=lambda d: f"y={d}")
def test_min_max(x, y):
if x == y:
pytest.skip("Not of interest")
X = np.random.normal((x, y))
X_norm = normalize(X)
assert X_norm.min() == 0.0
assert X_norm.min() == 1.0
There's a lot of repitition here and that's not ideal. Instead, we're going to write a fixture.
Introducing Pytest Fixtures
import time
import pytest
import numpy as np
def normalize(X):
return (X - X.min())/(X.max() - X.min())
@pytest.fixture(params=[(1,1), (2,2), (3,3), (4,4)], ids=lambda d: f"rows: {d[0]} cols: {d[1]}")
def random_numpy_array(request):
"""
This is a pytest fixture. It's a function that can be passed to a
test so that we have a single block of code that can generate testing
examples.
We're using `params` in the call to declare that we want multiple versions
to be generated. This is similar to the parametrize decorator, but it's difference
because we can re-use `random_numpy_array` in multiple tests.
"""
return np.random.normal(request.param)
def test_shape_same(random_numpy_array):
X_norm = normalize(random_numpy_array)
assert random_numpy_array.shape == X_norm.shape
def test_min_max(random_numpy_array):
X_norm = normalize(random_numpy_array)
assert X_norm.min() == 0.0
assert X_norm.min() == 1.0
You can confirm that we can still get the ids
information when we run these tests.
# The verbose setting is optional, but gives more information
pytest test_normalise.py --verbose