{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/python/pytest-param/",
  "description": "Write readable parametrized tests with pytest.param for better test names, conditional skips, custom IDs, and structured test data.",
  "path": "/python/pytest-param/",
  "publishedAt": "2024-08-28T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "Python",
    "Testing",
    "TIL",
    "Pytest"
  ],
  "textContent": "I love [pytest.mark.parametrize] - so much so that I sometimes shoehorn my tests to fit into\nit. But the default style of writing tests with parametrize can quickly turn into an\nunreadable mess as the test complexity grows. For example:\n\nThe polarify function converts Cartesian coordinates to polar coordinates. We're using\n@pytest.mark.parametrize in its standard form to test different conditions.\n\nHere, the list of nested tuples with inputs and expected values becomes hard to read as the\ntest suite grows larger. When the function under test has a more complex signature, I find\nmyself needing to do more mental gymnastics to parse the positional input and expected\nvalues inside parametrize.\n\nAlso, how do you run a specific test case within the suite? For instance, what if you want\nto run only the third case where x, y, expected = (0, 1, (1, 1.5707963267948966))?\n\nI used to set custom test IDs like below to be able to run individual test cases within\nparametrize:\n\nThis works, but mentally associating the IDs with the examples is cumbersome, and it doesn't\nmake things any easier to read.\n\nTIL, [pytest.param] gives you a better syntax and more control to achieve the same. Observe:\n\nWe're setting the unique IDs inside pytest.param. Now, any test can be targeted with\npytest's -k flag like this:\n\nThis will only run the second test case on the list.\n\nOr,\n\nThis will run the last two tests.\n\nBut the test is still somewhat hard to read. I usually refactor mine to take a kwargs\nargument so that I can neatly tuck all the input and expected values associated with a test\ncase in a single dictionary. Notice:\n\nEverything associated with a single test case is passed to pytest.param in a single\ndictionary, eliminating the need to guess any positional arguments.\n\nUsing pytest.param also allows you to set custom test execution conditionals, which I've\nstarted to take advantage of recently:\n\nIn the last block, pytest.param bundles test data with execution conditions. We're using\nxfail to mark a test as expected to fail, while skipif skips tests based on conditions.\nThis keeps all the logic for handling test cases, including failures and skips, directly\nalongside the test data.\n\n\n\n\n[pytest.mark.parametrize]:\n    https://docs.pytest.org/en/7.1.x/how-to/parametrize.html#parametrize-basics\n\n[pytest.param]:\n    https://docs.pytest.org/en/7.1.x/reference/reference.html#pytest-param",
  "title": "Taming parametrize with pytest.param"
}