Algorithms are defined by how they behave across inputs, not by one successful example. Parameterized tests run the same assertion against a collection of cases, keeping a useful set of examples readable. They complement the Big O analysis by checking correctness; performance claims still need separate measurements.
Define the contract first #
For a two-sum function, the contract might say: return indices of two distinct values whose sum equals the target, or return None if no such pair exists. Writing the contract makes it clear that duplicate values and a pair that reuses one index deserve explicit cases.
def two_sum(values, target):
seen = {}
for index, value in enumerate(values):
needed = target - value
if needed in seen:
return [seen[needed], index]
seen[value] = index
return None
Parameterize representative inputs #
import pytest
from solution import two_sum
@pytest.mark.parametrize(
"values, target, expected",
[
([2, 7, 11, 15], 9, [0, 1]),
([3, 3], 6, [0, 1]), # duplicate values, distinct indices
([1, 4, 6], 10, [1, 2]),
([1, 2, 3], 20, None), # no solution
([], 0, None),
],
)
def test_two_sum_returns_expected_indices(values, target, expected):
assert two_sum(values, target) == expected
Each row states an input and its expected outcome. If a case fails, pytest reports its parameter values so the defect is easier to reproduce.
Prefer cases that expose a rule #
Add examples for the contract’s important boundaries: empty input, the smallest valid input, repeated values, negative values, and absence of a solution. Avoid a large collection of nearly identical values that does not exercise a new rule.
Add property tests when the invariant is clear #
For some algorithms, a general property can check many generated inputs. Sorting, for instance, should preserve the input multiset and produce nondecreasing output. A property test can catch combinations that hand-selected examples miss, but it does not replace a few named examples that explain the intended behavior.
Keep random tests reproducible, and do not assert implementation details such as the exact order of equal elements unless stability is part of the contract.
Keep correctness and complexity separate #
A test suite can show that outputs are correct for selected cases. It does not prove that the implementation is O(n) or meets a latency target. Analyze the algorithm separately, then benchmark representative input sizes if runtime matters.