DuXeN0N
DuXeN0N

Reputation: 1587

Skip test depending on parameter in py.test

I have a test fixture with session scope which is parametrized, e.g.

@pytest.fixture(scope="session", params=["one", "two", "three"])
def myfixture():
    ...

In my directory, I have files which use pytest.mark.usefixtures("myfixture") and one file which contains tests should be run only for myfixture with "two" parameter and py.test should skip it otherwise.

Are there any ways to achieve this in py.test or do I need to set a special variable in some class in myfixture() function?

Upvotes: 11

Views: 7230

Answers (4)

ingomueller.net
ingomueller.net

Reputation: 4685

I have ended up writing my own decorator, which I think is reasonably easy and clean:

import functools

def skip_targets(excluded_values):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            paramA = kwargs['paramA']
            if paramA in excluded_values:
                pytest.skip('Param A "{}" currently not supported'.format(paramA))
            func(*args, **kwargs)
        return wrapper
    return decorator

To deactivate a test that uses a fixture for paramA, you can exclude the value X like this:

@skip_targets(['X'])
def test_param(self, paramA):
    pass

Upvotes: 0

Grant
Grant

Reputation: 1

The clean way to do this is to use the pytest_collection_modifyitems hook to modify your test collection before they are run

Given your fixture as described:

@pytest.fixture(scope="session", params=["one", "two", "three"])
def myfixture(request):
    ...

Add a custom mark to your tests:

@pytest.mark.restriction("two")
def test_smth(self, myfixture):
    ...

Then modify your test collection with custom deselection logic:

def pytest_collection_modifyitems(items, config):
    """ deselect test items which do not match the fixture restriction """
    deselection_items = []
    for item in items:
        # There may be a better way than regex to get the parameter
        passed_param = re.search(r'\[(.+?)\]', item.name).group(1)
        restrictions = set([mark.args[0] for mark in item.iter_markers(name='restriction')])
        if len(restrictions) > 0:
            if passed_param not in restrictions:
                deselection_items.append(item)
    items[:] = [item for item in items if item not in deselection_items]
    config.hook.pytest_deselected(items=deselection_items)

Upvotes: 0

Rob Buckley
Rob Buckley

Reputation: 758

I came across this question while trying to solve a similar use case. My solution, in case it helps anyone avoid couple of hours. It's an easy way to override the parameterisation of a fixture from a test:

@pytest.fixture(scope="session", params=["one", "two", "three"])
def myfixture():
    ...

@pytest.mark.parameterize('myfixture', ['two'], indirect=True)
def test1(myfixture):
    ...

def test2(myfixture):
    ...

Thanks to https://hackebrot.github.io/pytest-tricks/mark_parametrize_with_indirect/ for explaining the use of indirect!

Upvotes: 1

DuXeN0N
DuXeN0N

Reputation: 1587

Found solution myself, one can define function in conftest.py:

def pytest_namespace():
    return {"param": None}

And in fixture function we can do:

@pytest.fixture(scope="session", params=["one", "two", "three"])
def myfixture():
    pytest.param = request.param
    # ...

So we can wrap test class with:

@pytest.mark.skipif("pytest.param == 'value'")
class TestSmth(object):
    ...

Upvotes: 15

Related Questions