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 parametrizing a fixture by multiple yields. #2155

Closed
kdexd opened this issue Dec 21, 2016 · 1 comment
Closed

Pytest parametrizing a fixture by multiple yields. #2155

kdexd opened this issue Dec 21, 2016 · 1 comment

Comments

@kdexd
Copy link

kdexd commented Dec 21, 2016

I need to parametrize a test which requires tmpdir fixture to setup different testcases.

Suppose initial test looks like this:

def test_foo(tmpdir):
    obj1 = SomeObject1(path=tmpdir.strpath, mode='r')
    assert obj1.foo == 'bar0'

    obj2 = SomeObject2(path=tmpdir.strpath, mode='wb')
    assert obj2.foo == 'bar1'

    obj3 = SomeObject3(path=tmpdir.strpath, mode='default')
    assert obj3.foo == 'bar2'

I can't use tmpdir in parametrize, so either I would prefer indirect parametrization, or parametrization through a fixture.

Alternative 1: Indirect Parametrization

@fixture(scope='function')
def testcase_obj(tmpdir, request):
    objs = [SomeObject1(path=tmpdir.strpath, mode='r'),
            SomeObject2(path=tmpdir.strpath, mode='wb'),
            SomeObject3(path=tmpdir.strpath, mode='default')]
    return objs[request.param]

@parametrize(
    'testcase_obj, expected_foo',
    [(0, 'bar0'), (1, 'bar1'), (2, 'bar2')],
    indirect=['testcase_obj']
)
def test_foo(testcase_obj, expected_foo):
    assert testcase_obj.foo == expected_foo

Alternative 2: Parametrizing through fixture

@fixture(scope='function', params=range(3))
def testcase(tmpdir, request):
    objs = [SomeObject1(path=tmpdir.strpath, mode='r'),
            SomeObject2(path=tmpdir.strpath, mode='wb'),
            SomeObject3(path=tmpdir.strpath, mode='default')]
    expected_foos = ['bar0', 'bar1', 'bar2']
    return objs[request.param], expected_foos[request.param]

def test_foo(testcase):
    testcase_obj, expected_foo = testcase
    assert testcase_obj.foo == expected_foo

Alternative two is a little less entangled but one can easily miss a bug if he adds testcases in function body but forgets to update range(3).

Currently fixtures yield only once but it will be really great if multiple yields results in parametrization.
Example code would simply reduce to:

@fixture(scope='function')
def testcase(tmpdir, request):
    objs = [SomeObject1(path=tmpdir.strpath, mode='r'),
            SomeObject2(path=tmpdir.strpath, mode='wb'),
            SomeObject3(path=tmpdir.strpath, mode='default')]
    expected_foos = ['bar0', 'bar1', 'bar2']

    for obj, expected_foo in zip(objs, expected_foos):
        yield obj, expected_foo

def test_foo(testcase):
    testcase_obj, expected_foo = testcase
    assert testcase_obj.foo == expected_foo
@nicoddemus
Copy link
Member

Hi @karandesai-96!

Please refer to the discussion in #1595, but the gist is this:

... we have to obtain all items during the collection phase, and fixtures parametrized that way won't be executed until the first test that uses it executes, long past the collection phase.

I'm closing this for now, but feel free to ask for clarification!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants