-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
In many of my tests I've been using pytest.param(..., id="...") as means of providing more readable identifiers for my tests inline, since otherwise with multiple parameters, the autogenerated id is hard to read.
Some of my parameter-driven tests end up growing beyond 2-3 arguments and maintaining the parameters positionally becomes error-prone and hurts readability. The reader has to mentally "zip" a list of parameters (of a parameter set) to the parameter names defined earlier, and if you need to add a parameter, now you need to add them to each and every test, and if you want to insert it (e.g. to maintain grouping), you'd have to carefully count parameters in each of the sets.
One pattern I've been introducing in such cases is defining a "ParamSet" attrs base class:
import attr
@attr.s(auto_attribs=True, kw_only=True)
class ParamSet:
"""
Use as base class for sets for parameters for @pytest.mark.parametrize
when you find yourself passing too many parameters positionally.
"""
id: str
@property
def __name__(self) -> str: # indicate the id to pytest
return self.idFrom there, a test would look like:
@attr.s(auto_attribs=True, kw_only=True)
class MyComplexTestParamSet(ParamSet):
foo: int
bar: str = 'some_default'
expected_baz: float
@pytest.mark.parameterize('params', [
MyComplexTestParamSet(foo=42, expected_baz=42.42, id="happy path"),
...
])
def test_complex(params: MyComplexTestParamSet):
...Before we discuss specifics, is this something we'd want pytest.param to enable?
To give an idea, I'd imagine usage like:
@pytest.mark.parameterize('params', [
pytest.param(foo=42, expected_baz=42.42, id="happy path"),
...
])
def test_complex(params: Any):
...or something more typed like this: (I like the fact that the type hint enables better developer experience when working on the test)
class MyComplexTestParamSet(pytest.ParameterSet):
foo: int
...