Software Testing Best Practices

Chapter 2 · Practice 3 of 5

Use table driven tests for many inputs to the same logic

From Writing good unit tests

Why it matters

When the same behaviour needs checking with many inputs, copy pasting a test ten times hides the one line that differs. Every major runner supports parametrized tests: test.each in Vitest and Jest, @pytest.mark.parametrize in pytest, subtests with a table in Go.

Each row becomes its own test in the report, so a failure tells you exactly which input broke. Adding a new edge case is one new line.

Keep the table readable: put the input and expected output side by side, and give rows an id when the values alone do not explain the case.

Example

Python
import pytest
from shipping import shipping_cost

@pytest.mark.parametrize(
    "weight_kg, expected",
    [
        (0.5, 200),   # small parcel
        (1.0, 200),   # boundary: still small
        (1.01, 350),  # just over the boundary
        (20.0, 900),  # largest allowed
    ],
)
def test_shipping_cost_by_weight(weight_kg, expected):
    assert shipping_cost(weight_kg) == expected


def test_rejects_weight_over_limit():
    with pytest.raises(ValueError):
        shipping_cost(20.5)