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

pytest.mark.parametrize string-based parameter list doesn't handle single element tuples #719

Open
pytestbot opened this issue Apr 14, 2015 · 17 comments
Assignees
Labels
topic: parametrize related to @pytest.mark.parametrize type: bug problem that needs to be addressed

Comments

@pytestbot
Copy link
Contributor

Originally reported by: David Haney (BitBucket: david_haney, GitHub: david_haney)


When specifying a @pytest.mark.parametrize element composed of a one-element tuple, I'm unable to use the string-based argument list, for example:

@pytest.mark.parametrize("arg", scenarios)
def test(arg):
    print(arg)
    assert(arg == "a") # this assert always fails

arg ends up being the tuple instead of the first item in the tuple (as I would expected based on tuples with more than one item. I also tried:

@pytest.mark.parametrize("arg,", scenarios)

but that also associated the entire tuple with arg instead of the first element. I reverted back to the older model of specifying the arguments as a tuple:

@pytest.mark.parametrize("arg,", scenarios)

Finally I switched back to the older style of using a tuple to specify the parameter list:

@pytest.mark.parametrize(("arg",), scenarios)

This version worked. This seems to imply that there is either a bug/limitation in the new string-based parameter specification, or that there is still a use-case for the tuple-based parameter specification. It would be helpful if either the string-based implementation could be updated to handle this situation, or if the documentation could be updated to note when the tuple-based parameter specification is still needed.


@pytestbot
Copy link
Contributor Author

Original comment by Brianna Laugher (BitBucket: pfctdayelise, GitHub: pfctdayelise):


This looks closely related to #638.

@pytestbot pytestbot added the type: bug problem that needs to be addressed label Jun 15, 2015
@pfctdayelise pfctdayelise self-assigned this Jul 10, 2015
@RonnyPfannschmidt
Copy link
Member

@pfctdayelise is that one easy, its hard to tell off hand

@pfctdayelise pfctdayelise added the topic: parametrize related to @pytest.mark.parametrize label Jul 25, 2015
@untitaker
Copy link
Contributor

The current state:

  • Single-arg parametrization means a list of values must be passed.
  • Multi-arg parametrization means a list of value-tuples must be passed.

Unless I'm missing something, fixing this would create an ambiguity, as single-arg parametrization would take either values or tuples with one value each. It'd be no longer possible to pass tuples as parametrization values, i.e. it'd be no longer possible to replicate the current behavior of scenarios = [("a",)] in the future.

I think this issue should be closed -- the fix is to use scenarios = ['a'].

@nicoddemus
Copy link
Member

I think the OP means that these two tests should be equivalent:

import pytest

scenarios = [('a',)]

@pytest.mark.parametrize(("arg",), scenarios)
def test_foo_1(arg):
    assert arg == 'a'

@pytest.mark.parametrize("arg", scenarios)
def test_foo_2(arg):
    assert arg == 'a'  
test_foo.py::test_foo_1[a] FAILED
test_foo.py::test_foo_2[arg0] PASSED

================================== FAILURES ===================================
________________________________ test_foo_1[a] ________________________________

arg = 'a'

    @pytest.mark.parametrize(("arg",), scenarios)
    def test_foo_1(arg):
>       assert arg == ('a',)
E       assert 'a' == ('a',)

test_foo.py:7: AssertionError
===================== 1 failed, 1 passed in 0.02 seconds ======================

When you use more than one argument, both tests work as expected:

import pytest

scenarios = [('a', 'b')]

@pytest.mark.parametrize(("arg","arg2"), scenarios)
def test_bar_1(arg, arg2):
    assert arg == 'a'
    assert arg2 == 'b'

@pytest.mark.parametrize("arg,arg2", scenarios)
def test_bar_2(arg, arg2):
    assert arg == 'a'
    assert arg2 == 'b'  
test_foo.py::test_bar_1[a-b] PASSED
test_foo.py::test_bar_2[a-b] PASSED

I would expect both forms to work even if used with a single argument, so this seems like a bug to me... 😅

@untitaker
Copy link
Contributor

I see, I misread the OP.

@RonnyPfannschmidt
Copy link
Member

I would expect tuples for single arg string without the comma

@nicoddemus
Copy link
Member

I would expect tuples for single arg string without the comma

Could you elaborate your reasoning?

From the docs, it seems that passing "arg" or ("arg",) should be the same thing, that's why I feel it is a bug.

@RonnyPfannschmidt
Copy link
Member

think about a use-cases where you want to pass tuples of different arty to a single argument,

wouldn't it unpack them in a bad way ? - imho the structure of the definition should match the structure of the expression

the ',' writing is just a shortcut for the tuples - and just like tuples without a comma, its not a tuple

@nicoddemus
Copy link
Member

Could you post a code example of what you mean? I think it would be better to illustrate your point.

@RonnyPfannschmidt
Copy link
Member

paramerize('a', [(1,), (1,2), (2,), ]) # a should never be 1 and never be 2

@untitaker
Copy link
Contributor

I thought this issue was about passing tuples for the parameter NAMES, not the VALUES.

On 8 August 2015 18:44:46 CEST, Ronny Pfannschmidt [email protected] wrote:

paramerize('a', [(1,), (1,2), (2,), ]) # a should never be 1 and never
be 2

Reply to this email directly or view it on GitHub:
#719 (comment)

Sent from my phone. Please excuse my brevity.

@RonnyPfannschmidt
Copy link
Member

@untitaker there is some detail logic involved wrt interaction between names and values, it'd take a while to dig this out properly, i cant promise it for this weekend

@bluetech
Copy link
Member

bluetech commented Jun 1, 2020

IMO this is the desired behavior:

import pytest

scenarios = [('a',)]

@pytest.mark.parametrize(("arg",), scenarios)
def test_tuple(arg):
    assert arg == 'a'

@pytest.mark.parametrize("arg", scenarios)
def test_string(arg):
    assert arg == ('a',)

@pytest.mark.parametrize("arg,", scenarios)
def test_stringtuple(arg):
    assert arg == 'a'

Out of these, test_stringtuple fails.

@RonnyPfannschmidt
Copy link
Member

@bluetech thanks for picking this up and detailing whats needed

@donfiguerres
Copy link

Is there any progress with this issue?

@The-Compiler
Copy link
Member

@donfiguerres if there was, you would see it in here.

@Hawk777
Copy link
Contributor

Hawk777 commented May 12, 2023

I’m not actually sure what the plan is right now on this ticket. Personally I don’t care all that much what the behaviour is; however, I would very much like it if the documentation matched the behaviour, which it doesn’t.

# argnames argnames as list or string what to pass as argvalues according to docs what you actually have to pass
1 string list of values list of values
1 list list of values list of tuples
>1 string list of tuples list of tuples
>1 list list of tuples list of tuples

Even if the plan is to eventually come up with a new rule and change the behaviour, for now, could the documentation be changed to match the actual behaviour, which is that you have to pass a list of tuples if argnames has more than one argname or is a list, even if it has only one element in it?

(when I say “list”, I mean any iterable that isn’t a string)

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: bug problem that needs to be addressed
Projects
None yet
Development

No branches or pull requests

9 participants