pytest-dev / pytest

The pytest framework makes it easy to write small tests, yet scales to support complex functional testing
https://pytest.org
MIT License
12.03k stars 2.67k forks source link

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

Open pytestbot opened 9 years ago

pytestbot commented 9 years ago

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 commented 9 years ago

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


This looks closely related to #638.

RonnyPfannschmidt commented 9 years ago

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

untitaker commented 9 years ago

The current state:

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 commented 9 years ago

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... :sweat_smile:

untitaker commented 9 years ago

I see, I misread the OP.

RonnyPfannschmidt commented 9 years ago

I would expect tuples for single arg string without the comma

nicoddemus commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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

RonnyPfannschmidt commented 9 years ago
paramerize('a', [(1,), (1,2), (2,), ]) # a should never be 1 and never be 2
untitaker commented 9 years ago

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 notifications@github.com 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: https://github.com/pytest-dev/pytest/issues/719#issuecomment-129010212

Sent from my phone. Please excuse my brevity.

RonnyPfannschmidt commented 9 years ago

@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 commented 4 years ago

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 commented 4 years ago

@bluetech thanks for picking this up and detailing whats needed

donfiguerres commented 1 year ago

Is there any progress with this issue?

The-Compiler commented 1 year ago

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

Hawk777 commented 1 year ago

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)