Alex
Alex

Reputation: 44345

How to access fixture's values as input to parametrized test

In py.test I need to dynamically define tests, depending on tests I defined in a file.

So what I was thinking is to define a fixture in conftest.py that reads the file and returns a dictionary with the tests.

File tests.json:

{
    "test1": "text",
    "test2": "42",
    "test3": 1
}

I then define a fixture in conftest.py to return the dictionary with the tests:

def pytest_addoption(parser):
    parser.addoption(
        "--tests",
        default="tests.json",
    )

@pytest.fixture
def mytests(request):
    testfile = request.config.getoption("--tests")
    with open(testfile) as f:
        tests = json.load(f)
    return tests

and then I can use a parametrized test as follows in test_pytest.py:

@pytest.mark.parametrize("test_name", [(key) for key, value in mytests.items()])
def test1(test_name):
    print(test_name)

which does not work as, at this point, py.test does not seem to 'know' that mytests is a fixture. I get an error

E   NameError: name 'mytests' is not defined

How to handle this correctly? I just want to be able to either run all the test that are defined in the json file, or to be able to select a single test from it with the -k option if py.test.

How to do it?


Based on some comments given below I tried to implement something as follows:

@pytest.hookimpl
def pytest_generate_tests(metafunc):
    if "myparam" in metafunc.fixturenames:
        with open(metafunc.config.option.tests) as f:
            tests = json.load(f)

        # add parametrization for each fixture name
        for name, value in tests.items():
            print(name, value)
            metafunc.parametrize("mparam", (name, value))

def test1(myparam):
    print(myparam)

But with this I got an error

ERROR test_pytest.py - ValueError: duplicate 'myparam'

Upvotes: 0

Views: 683

Answers (1)

MrBean Bremen
MrBean Bremen

Reputation: 16815

As mentioned in the comments, you cannot use a fixture in mark.parametrize. Fixtures can only be used in test functions and other fixtures.

To have dynamic parametrization like in this case, you can implement the hook function pytest_generate_tests:

@pytest.hookimpl
def pytest_generate_tests(metafunc):
    if "test_name" in metafunc.fixturenames:
        testfile = metafunc.config.getoption("--tests")
        with open(testfile) as f:
            tests = json.load(f)
        metafunc.parametrize("test_name", tests.items())


def test1(test_name):
    print(test_name)

This will parametrize all tests with a "test_name" argument (e.g. fixture) with the items in the config file.

Running this with the given json file will result in something like:

$ python -m pytest -s
...
collected 3 items

test_pytest.py ('test1', 'text')
.('test2', '42')
.('test3', 1)
.

Upvotes: 2

Related Questions