pytest parameter matrices

A few months ago I explained how I efficiently test Django forms with pytest parameterization. Last week, I learned a new trick from Raphael Pierzina's post about ids for fixtures and parametrize, which is:

You you can add multiple parametrization markers to a test function which then create a test parameter matrix.

The list of test cases can thus be written much more clearly. Compare the example code from my previous post:

from django import forms

import pytest


class ExampleForm(forms.Form):
    name = forms.CharField(required=True)
    age = forms.IntegerField(min_value=18)


@pytest.mark.parametrize(
    'name, age, validity',
    [('Hugo', 18, True),
     ('Egon', 17, False),
     ('Balder', None, False),
     ('', 18, False),
     (None, 18, False),
     ])

def test_example_form(name, age, validity):
    form = ExampleForm(data={
        'name': name,
        'age': age,
    })

    assert form.is_valid() is validity

to the same test using multiple parameterization markers:

@pytest.mark.parametrize(
    'name, valid_name',
    [
        ('Hugo', True),
        ('', False),
        (None, False),
    ]
)
@pytest.mark.parametrize(
    'age, valid_age',
    [
        ('18', True),
        ('17', False),
        (None, False),
    ]
 )
def test_example_form(name, age, valid_name, valid_age):
    form = ExampleForm(data={
        'name': name,
        'age': age,
    })

    assert form.is_valid() is (valid_name and valid_age)

This tests all 9 possible combinations of the three test cases each for name and age. Maybe that's overkill, but on the other hand I can be sure that I touch all the relevant combinations.

Take note of the last line that checks that the form only validates when both input parameters are valid.

The main advantage I see is legibility. For each form field, I only have to understand the list of test parameters for exactly that field, and not any additional combinations with other fields.

I'm loving it!