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

How to provide fixture parameters at test level? #2502

Closed
ghost opened this issue Jun 14, 2017 · 3 comments
Closed

How to provide fixture parameters at test level? #2502

ghost opened this issue Jun 14, 2017 · 3 comments
Labels
type: question general question, might be closed after 2 weeks of inactivity

Comments

@ghost
Copy link

ghost commented Jun 14, 2017

Hello!

My use case for pytest is for functional testing. What I am dealing on a daily basis is deployed backend applications on real or virtual servers. System Under Test is probed via combination of SSH and REST API calls. I have come across a pattern that I frequently have to use (see simple example below) and was wondering if I am not reinventing the wheel or perhaps there is some idiomatic way to approach this.

I frequently have fixtures that need to be parametrized at test time. These fixtures usually are just "proxies" that instantiate some objects. So far nothing out of ordinary. But Pytest's approach to fixture parametrization is not suitable for me (or so it seems) since I require a mechanism of passing parameters to objects being created by fixtures.

What I have come up is marking test function with a special mark which is used to pass parameters to fixture.

Again, I am not sure if this is the idiomatic way, anyone care to comment or suggest a better approach? Will appreciate any comments!

@pytest.mark.sut_params(role='some_role')
def test_example1(sut):
    assert sut.role == 'some_role'

@pytest.mark.sut_params(name='sut1-uat')
def test_example2(sut):
    assert sut.name == 'sut1-uat'

def test_example3(sut):
    pass

@pytest.fixture
def sut(request):
    if not hasattr(request.function, 'sut_params'):
        raise ValueError('Need to specify sut parameters')
    sut = Sut(**request.function.sut_params.kwargs)
    yield sut
    sut.cleanup()

class Sut:
    """
    Represents system under test

    Provides high-level methods for interaction
    """
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def cleanup(self):
        print('DO CLEANUP')
@nicoddemus
Copy link
Member

If your parameters are available at module import time (which seems to be the case by your example), you can use the indirect parameter of parametrize.

import pytest

@pytest.mark.parametrize('sut', [dict(role='some_role')], indirect=True)
def test_example1(sut):
    assert sut.role == 'some_role'

@pytest.mark.parametrize('sut', [dict(name='sut1-uat')], indirect=True)
def test_example2(sut):
    assert sut.name == 'sut1-uat'

def test_example3(sut):
    pass

@pytest.fixture
def sut(request):
    if not hasattr(request, 'param'):
        raise ValueError('Need to specify sut parameters')
    sut = Sut(**request.param)
    yield sut
    sut.cleanup()

class Sut:
    """
    Represents system under test

    Provides high-level methods for interaction
    """
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def cleanup(self):
        print('DO CLEANUP')

Alternatively, I would just make the fixture return a factory which is simpler and more flexible for your use case IMHO:

import pytest

def test_example1(create_sut):
    sut = create_sut(role='some_role')
    assert sut.role == 'some_role'


def test_example2(create_sut):
    sut = create_sut(name='sut1-uat')
    assert sut.name == 'sut1-uat'

def test_example3(create_sut):
    pass

@pytest.fixture
def create_sut():
    instances = []
    def create_sut(**kwargs):
        s = Sut(**kwargs)
        instances.append(s)
        return s
    yield create_sut
    for s in instances:
        s.cleanup()

class Sut:
    """
    Represents system under test

    Provides high-level methods for interaction
    """
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def cleanup(self):
        print('DO CLEANUP')

@nicoddemus nicoddemus added the type: question general question, might be closed after 2 weeks of inactivity label Jun 14, 2017
@ghost
Copy link
Author

ghost commented Jun 14, 2017

@nicoddemus Great examples! Frankly I had hard time understanding indirect parametrize from documentation. I think now I get it. So thanks for that!

Coincidently my initial approach to the problem was very much similar to your second example with factory. I still have some dislike but that is mainly cosmetic and very subjective.

@nicoddemus
Copy link
Member

No problem, feel free to follow on the approach you think it is best. 👍

If you don't have more questions could you please close this then?

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question general question, might be closed after 2 weeks of inactivity
Projects
None yet
Development

No branches or pull requests

1 participant