-
Use pytest.mark.parametrize for categorical input values. For Literal[...], parametrize over every possible literal value. For ordinal or discrete inputs such as n_observations or n_recommendations, use sensible ranges: 0 if allowed, otherwise the minimum; a normal value such as 10; and a large cheap value such as 1000.
-
For multiple parametrized variables, use one @pytest.mark.parametrize decorator per variable. Stacked parametrization produces the exhaustive Cartesian product of all provided values.
@pytest.mark.parametrize("metric", ["cosine", "euclidean"])
@pytest.mark.parametrize("n_recommendations", [1, 10, 1000])
def test_recommendations(metric, n_recommendations):
...
This example produces six test cases:
("cosine", 1)
("cosine", 10)
("cosine", 1000)
("euclidean", 1)
("euclidean", 10)
("euclidean", 1000)
-
Mock external APIs and I/O only. Do not use mocking to abstract away components whose behaviour needs to be tested.
-
Use fixtures for reusable toy data, mocked objects, shared expected values, or any object referenced in more than one test. Define all fixtures in tests/conftest.py, never directly in a test module. Define tiny one-off toy data inside the test.
-
Never define nested functions unless scope requires it, such as nested generator builders.
-
Avoid top-level helper functions in test modules. Prefer simple tests that call existing code. Add helpers only when a test would otherwise become complex or repetitive.
-
Do not test class initialization. Do not assert only that an attribute exists or equals the value just passed into initialization.
-
For shaped outputs, explicitly assert shape-related properties such as length, dimensions, row counts, column counts, tensor shapes, dataframe shapes, collection sizes, or whether the output shape should match or differ from the input shape.
-
Test the intent behind a function or method, not its implementation details, attributes, or internal structure. Understand what the code is trying to achieve and validate that behaviour.
-
Do not add assertion messages:
assert len(result) == expected_length
not:
assert len(result) == expected_length, "Unexpected result length"
-
Keep comments rare and only use them to explain non-obvious assertions or test scenarios.
-
Each unit test should be a standalone function. Do not use test classes or self.
-
For mathematical functions, understand the derivations and test assumptions, invariants, constraints, theoretical properties, and expected outputs.
-
Keep the test suite lean. Prioritize tests that provide meaningful documentation, regression protection, or edge-case coverage relative to their maintenance cost.
-
Avoid repetitive tests. When multiple tests share the same setup and only differ in assertions, combine them into a single behaviour-focused test where appropriate.
-
Do not use try/except in tests. Tests should fail with their original traceback.