Post/Code

HomeAboutUsesNow

Pytest Parametrize.

Pytest is an intuitive and easy to use testing library for writing Python unit tests. One incredibly useful feature for streamlining your tests is the @pytest.mark.parametrize decorator.

When you are writing tests, you will often need to write tests to cover several similar edge cases. For example, when parsing POST requests and validating their content. In such situations, you will often have several very similar but slightly different scenarios that you want to cover in your testing. It can be tedious to write out these very similar, but different tests. This is where parametrize steps in!

Lets say you have a test for a user registration route - /auth/register for example. To thoroughly test such an endpoint, you may want to make several posts with minor changes in both the request body and in the response body to represent a range of possible error scenarios. In order to do so, you can do the following:

@pytest.mark.parametrize(
    "payload, message",
    [
        ({}, "Invalid payload"),
        (
            {"email": "test", "password": "password"},
            "Please provide a valid email address",
        ),
        ({"email": "test@test.com"}, "Invalid payload, please provide a 'password' field."),
        ({"password": "password"}, "Invalid payload, please provide an 'email' field."),
        ({"email_address": "test@test.com"}, "Invalid payload, please provide an 'email' field"),
        ({"email": "test@test.com", "pass": "password"}, "Invalid payload, please provide a 'password' field"),
    ],
)
def test_user_registration_invalid_payload(test_app, test_db, payload, message):
    # test_app, and test_db are pytest fixtures passed to the test
    # payload and message are the 'parametrized' variables used in the test
    # recreate_db is a utility function to clear out the test db
    recreate_db()
    client = test_app.test_client()
    res = client.post(
        "/auth/register",
        data=json.dumps(payload),
        content_type="application/json",
    )
    data = json.loads(res.data.decode())
    assert res.status_code == 400
    assert res.content_type == "application/json"
    assert message in data["message"]
    assert not data["status"]

In the above test, instead of manually creating a test for each scenario, we can easily use parametrize to wrap up the post bodies and response messages in one easy to use place. From there, we just swop out the variables payload and message in the appropriate places in the test. That is to say, add payload into the test_client's post data, and add message into the relevant assert to check that the correct response message is returned for the scenario.

That's it!

Parametrize is a quick, easy to use and understand feature of Pytest. I encourage you to try it out!