When we write tests for code, we’re often faced with repeating the same tests multiple times, just with different inputs and expected results. Writing separate tests each time makes our test suites bulky, hard to maintain, and prone to errors. This is where parametrized tests – also known as data-driven testing – come to your rescue.
In this article, we’ll explore parametrized tests in Pytest in detail. We will walk through their significance, implementation, and practical examples that clearly demonstrate how you can leverage this powerful feature in real-world projects.
- What are parametrized tests?
- Advantages of Parametrized Tests
- A Simple Test Example (Without Parametrization)
- Simplified Data-Driven Testing with Pytest Parametrization
- Running Parametrized Tests
- Use Parametrize IDs for Better Test Output and Readability
- Using Variables for Parametrization Data
- Stacking Parametrized Decorators
- Some common mistakes
Let’s discuss them one by one.
What are parametrized tests?
Parametrized tests in Pytest enable you to run one test function multiple times with different inputs and expected outputs. Rather than writing separate tests for each combination of parameters, you define a single test and provide Pytest with the sets of inputs you wish to test. Pytest will automatically run the test function for each given scenario.
Advantages of Parametrized Tests
- Reduced code redundancy: Write a single test function instead of multiple, very similar tests.
- Improved readability: Parametrization clearly separates testing logic from test data.
- Ease of maintenance: Update and maintain test scenarios easily by just editing parameters.
A Simple Test Example (Without Parametrization)
To better understand parametrization, let’s look at an example scenario. Suppose you have a simple function that checks if a given number is even or not:
1 2 3 | # simple function to check if number is even def is_even(num): return num % 2 = = 0 |
Now, we want to find out if some numbers are even or not.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # simple function to check if number is even def is_even(num): return num % 2 = = 0 # test methods def test_one_even(): assert is_even( 4 ) = = True def test_one_odd(): assert is_even( 5 ) = = False def test_another_even(): assert is_even( 10 ) = = True def test_another_odd(): assert is_even( 11 ) = = False |
Once you run the above test file, you’ll notice that all the test cases have been executed.

Everything ran just fine, right? So, what’s the issue?
In the example above, we’ve written the same test multiple times, each with different test data. Now, imagine having to do this for hundreds or even thousands of data points – it would become really hard to manage. To avoid repeating code and keep things clean and efficient, we can make use of parameterization.
Simplified Data-Driven Testing with Pytest Parametrization
Pytest allows you to define parametrized tests using the @pytest.mark.parametrize
decorator. Here’s an improved version of the previous tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pytest from mymodule import is_even @pytest .mark.parametrize( "input_number, expected_output" , [ ( 4 , True ), ( 5 , False ), ( 10 , True ), ( 11 , False ), ] ) def test_is_even(input_number, expected_output): assert is_even(input_number) = = expected_output |
Understanding the Parametrized Decorator
The @pytest.mark.parametrize
decorator takes two main parameters:
- String of comma-separated variable names (
"input_number, expected_output"
): This includes arguments your test receives. - List of tuples: Each tuple serves as a single test scenario, containing input values and expected results.
For instance:
1 2 3 4 5 | [ ( 2 , True ), # checking even number ( 3 , False ), # checking odd number ( - 1 , False ), # checking negative odd number ] |
- Pytest handles the iteration automatically, making it ideal for data-driven test automation.
Pytest behaves as though you’ve written four separate tests, running test_is_even()
individually for each data set provided.
Running Parametrized Tests
Now, let’s run the test cases using the pytest -v
command. The -v
flag isn’t mandatory, but it helps by displaying the names of the test cases in the console output.

As you can see, the test case was executed four times, each time with a different set of test data.
Use Parametrize IDs for Better Test Output and Readability
When you run hundreds of tests, readability matters. You can define custom IDs for each test case scenario explicitly to make each one even more readable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import pytest # simple function to check if number is even def is_even(num): return num % 2 = = 0 @pytest .mark.parametrize( "input_number, expected_output" , [ ( 4 , True ), ( 5 , False ), ( 10 , True ), ( 11 , False ), ], ids = [ "Test number 4" , "Test number 5" , "Test number 10" , "Test number 11" , ] ) def test_is_even(input_number, expected_output): assert is_even(input_number) = = expected_output |
Output –
collected 4 items
tests/test_1.py::test_is_even[Test number 4] PASSED [ 25%]
tests/test_1.py::test_is_even[Test number 5] PASSED [ 50%]
tests/test_1.py::test_is_even[Test number 10] PASSED [ 75%]
tests/test_1.py::test_is_even[Test number 11] PASSED

This helps quickly identify failed test cases during CI/CD runs.
Using Variables for Parametrization Data
Separating the test cases into variables improves maintainability:
1 2 3 4 5 6 7 8 9 10 11 | import pytest # simple function to check if number is even def is_even(num): return num % 2 = = 0 even_odd_data = [( 2 , True ), ( 3 , False ), ( 0 , True ), ( - 1 , False )] @pytest .mark.parametrize( "number,result" , even_odd_data) def test_is_even(number, result): assert is_even(number) = = result |
Output –

Stacking Parametrized Decorators
Normally, you would use a single parameterized decorator like this:
1 2 3 4 5 6 7 8 9 10 11 12 | import pytest # simple function to check if number is even def is_even(num): return num % 2 = = 0 @pytest .mark.parametrize( "number, expected_result" , [ ( 2 , True ), ( 3 , False ), ]) def test_is_even(number, expected_result): assert (number % 2 = = 0 ) = = expected_result |
But what if you have multiple independent parameters, and you want your test to run against all possible combinations of those parameters? This is exactly where stacking comes in handy.
Stacking simply means placing more than one @pytest.mark.parametrize
decorator on a test function.
Simple Example of Stacking Parameterized Decorators
Let’s look at a clear example to understand how stacking works practically.
1 2 3 4 5 6 7 8 | import pytest @pytest .mark.parametrize( "number" , [ 1 , 2 , 3 ]) @pytest .mark.parametrize( "multiplier" , [ 10 , 100 ]) def test_multiply(number, multiplier): result = number * multiplier # Simple check to ensure the multiplication is correct assert result = = number * multiplier |
How does Pytest execute this?
Pytest generates all possible combinations of parameters listed, running each combination as a separate test.
The above example generates 3 numbers (1, 2, 3) x 2 multipliers (10, 100) = 6 distinct tests:
- (number=1, multiplier=10)
- (number=1, multiplier=100)
- (number=2, multiplier=10)
- (number=2, multiplier=100)
- (number=3, multiplier=10)
- (number=3, multiplier=100)
Let’s run 'pytest -v'
to clearly see the output:

Notice how Pytest generated and ran every combination automatically.
Some common mistakes
Mismatch between arguments defined in your decorator and the test method.
Suppose you have mistakenly provided fewer or more arguments in the parametrized decorator than your test method expects. Here’s what this mistake might look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pytest # simple function to check if number is even def is_even(num): return num % 2 = = 0 @pytest .mark.parametrize( "number, expected_result" , # Two parameters provided [ ( 2 , True , "extra-value" ), # Oops! Three values given instead of two ( 3 , False , "extra-value" ), ] ) def test_is_even(number, expected_result): assert number % 2 = = 0 = = expected_result |
Running this test produces an error
tests/test_1.py::test_is_even: in "parametrize" the number of names (2):
['number', 'expected_result']
must be equal to the number of values (3):
(2, True, 'extra-value')
==============short test summary info =================================
ERROR tests/test_1.py - Failed: tests/test_1.py::test_is_even: in "parametrize" the number of names (2):

How to Easily Fix:
Ensure that each parameter set has the exact number of arguments matching the names defined in your decorator:
Different Variable Names Between Decorator and Test Function
Another common mistake occurs when you name variables differently between your decorator and test method. Pytest cannot map arguments by position alone – it specifically uses the argument names.
1 2 3 4 5 6 7 8 9 10 11 12 | import pytest # parametrization has variables named "num, expected" @pytest .mark.parametrize( "num, expected" , [ ( 4 , True ), ( 7 , False ) ] ) def test_is_even(number, expected_result): # function arguments are different! assert (number % 2 = = 0 ) = = expected_result |
Running this test yields an error:
In test_is_even: function uses no argument 'num'
============================ short test summary info =============================================
ERROR tests/test_1.py - Failed: In test_is_even: function uses no argument 'num'

Why?
This happens because the decorator tries to send the arguments "num, expected"
to the function, but your function expects "number, expected_result"
. Pytest doesn’t know you intended these to mean the same thing.
How to Fix:
Simply make sure parameter names match exactly.
This is it. We hope that you have liked the article. If you have any doubts or concerns, please write to us in the comments or mail us at admin@codekru.com.