tezvyn:

Run One Test with Many Inputs using pytest.parametrize

AI-drafted, machine-checkedSource: docs.pytest.orgintermediate

Run one test function with many inputs using `@pytest.mark.parametrize`, avoiding repetitive code. It's ideal for checking a function against various inputs, edge cases, and expected failures. The footgun: mutable parameters like lists are passed by reference.

WHY IT EXISTS: Testing a function often requires checking it against many different inputs. Writing a separate test function for each input leads to a lot of boilerplate code, making the test suite hard to read and maintain. Parameterization was created to solve this by running the same test logic with different data.

THE MENTAL MODEL: Think of @pytest.mark.parametrize as a for loop that runs on top of your test function. You define a list of data sets (the inputs and expected outputs), and pytest executes your test once for each set, injecting the data as arguments. This keeps your test logic DRY (Don't Repeat Yourself) and your data separate from your test implementation.

HOW IT WORKS: You apply the @pytest.mark.parametrize decorator directly above a test function. The decorator takes two primary arguments: first, a string containing comma-separated names for the arguments your test function will receive, and second, a list of tuples. Each tuple represents one complete set of arguments for a single test run. Pytest generates a unique test for each tuple, making it easy to see which specific data set caused a failure. A key detail: if you pass a mutable object like a list or dict, it is passed by reference. Any modification inside the test will persist for subsequent runs using that same object, which can lead to confusing, order-dependent failures.

WHEN TO USE IT: Parameterization is ideal for data-driven testing. Use it when you need to verify a function's behavior across a range of inputs. This includes testing multiple valid inputs, boundary conditions (like empty lists, zero, or null values), and different inputs that should all produce the same expected outcome.

WHEN NOT TO USE IT: If the test logic itself needs to change significantly for different inputs, separate test functions are clearer. For very complex or dynamic test generation scenarios, especially those that depend on configuration or other fixtures, the pytest_generate_tests hook provides more advanced control than this decorator.

ONE CANONICAL EXAMPLE: Consider a test for Python's eval function. import pytest @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)]) def test_eval(test_input, expected): assert eval(test_input) == expected This will run test_eval three times. The first two runs will pass. The third run will fail because eval("6*9") is 54, not 42. Pytest will report this specific failure as test_eval[6*9-42], clearly showing the input values that caused the test to fail while allowing the other cases to pass.

Read the original → docs.pytest.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.