Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Parametrization Id Function Receives Arg Tuple #8283

Open
internetofjames opened this issue Jan 26, 2021 · 6 comments
Open

Test Parametrization Id Function Receives Arg Tuple #8283

internetofjames opened this issue Jan 26, 2021 · 6 comments
Labels
topic: parametrize related to @pytest.mark.parametrize type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature

Comments

@internetofjames
Copy link

I've seen requests similar to this, but I don't believe the exact same. Forgive me if this is a duplicate.

Proposal

For multi-argument parametrized test functions, pass the arguments as the original tuple to ids when ids` is a callable.

@pytest.mark.parametrize(
    "input_val, expected_output_val",
    [
        (0, 1),
        ("hello", "world")
    ],
    ids=lambda vals: f"Input val: {vals[0]}, expected output val: {vals[1]}"
)
def test_some_function(input_val, expected_ouput_val):
   ...

would output:

test_something.py::test_some_function[Input val: 0, expected output val: 1]
test_something.py::test_some_function[Input val: hello, expected output val: world]

Current Workarounds

If each arg in a given arg set is a different type, the above output could be achieved by using conditional statements based on arg type. e.g. ids=lambda val: f"Input val: {val} if type(val) == str else f"expected output val: {val}".

However, this is only sometimes the case. Exceptions could be found if a function accepts multiple types, or the likelihood of unique types for each parameter decreases with increasing number of parameters for a given function.

The other workaround I have is to declare the argument sets as a list, pass the list to argvals, and then pass a list comprehension to ids:

some_function_parametrize_args = [
        (0, 1),
        ("hello", "world")
]

@pytest.mark.parametrize(
    "input_val, expected_output_val",
    some_function_parametrize_args,
    ids=[f"Input val: {vals[0]}, expected output val: {vals[1]}" for vals in some_function_parametrize_args]
)
def test_some_function(input_val, expected_ouput_val):
   ...

This workaround works just fine, but I think it makes the code messier by exposing the args passed into argvals as a variable in the module when the intended scope would be just for that test function.


Thank you in advanced for reading this and discussing with me!

@Zac-HD Zac-HD added topic: parametrize related to @pytest.mark.parametrize type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature labels Jan 27, 2021
@bluetech
Copy link
Member

So you want to be able to generate an ID based on the entire instance of the parametrization at once, instead of for one component of the parametrization at a time and pytest combining it on its own. I think that's quite sensible.

But your proposal as-is would be a breaking change. An alternative proposal would be needed if we want to support this.

@asottile
Copy link
Member

Here's a way you can accomplish this without needing a change in pytest:

def myparam(val1, val2):
    return pytest.param(val1, val2, id=f"Input val: {val1}, expected output val: {val2}")


@pytest.mark.parametrize(
    ('input_val', 'output_expected_val'),
    (
        myparam(0, 1),
        myparam('hello', 'world'),
    ),
)
def test_whatever(input_val, output_expected_val):
    ...

@internetofjames
Copy link
Author

@bluetech I see. Yes, to me, that each parameter set (i.e. arguments defined by a single pytest.param - the ParameterSet object) should be treated as such - a single object, rather than individual values that are passed to the id function. Do you have suggestions on how might I amend the proposal?

This is my first time making a pytest proposal and I am really enjoying this discussion, thank you!

@internetofjames
Copy link
Author

@asottile I appreciate the suggestion! I think that defining a function that returns a ParameterSet within defeats the purpose of limiting the scope of the parameter set to that specific test function. Whether defining a list or a function at the module level, that is still forcing me to write extra code that lives outside of the scope of its intended use. However, if it is a function that you would reuse across tests, then absolutely!

@asottile
Copy link
Member

yeah there's nothing stopping you from putting this in a module and importing it -- I just wrote it inline because that was the easiest for a github comment

@bluetech
Copy link
Member

I can't think of any clean proposal myself. I think @asottile's suggestion is pretty good. I agree that having to define a separate function is not as nice as an inline lambda, but if you define it just above just above the parametrization I should be clear enough what's going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: parametrize related to @pytest.mark.parametrize type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature
Projects
None yet
Development

No branches or pull requests

4 participants