Anthony O
Anthony O

Reputation: 653

How can I make a factory for class-scope fixtures with pytest?

I have a set of fixtures that all do the same thing, open up a json file and make it available to a test class.

@pytest.fixture(scope="class")
def category_params(request):
    base_path = os.path.abspath(..)
    path = os.path.join(base_path, "data/category_params.json")
    with open(path, "r") as fp:
        category_params = json.load(f)
    return category_params

I repeat this set up for all of my different test categories in the test domain. It seems like something that I ought to be writing a single function for, but I am unsure as to how to use pytest to accomplish that. I believe I should be asking how to make a fixture factory, that is a pytest fixture object that makes other fixture objects.

Upvotes: 2

Views: 2246

Answers (2)

9000
9000

Reputation: 40884

(Warning: I did not test this code.)

I think something like this should work.

def make_fixture_for(data_file):
    @pytest.fixture(scope="class")
    def params_fixture(request):
        base_path = os.path.abspath(..)
        path = os.path.join(base_path, data_file)
        with open(path, "r") as fp:
            return json.load(fp)
    return params_fixture

foo_params = make_fixture_for("data/foo.json")
bar_params = make_fixture_for("data/bar.json")

This defines fixture functions at the module's top level, where pytest expects them, but does so in a sort of compact way.

This can be done in one loop over data file names and some mucking with globals to put them into the module's top-level context, but it would feel like impenetrable magic. The code above is very trivial.

Maybe pytest would not like a lot of fixture functions having the same name internally; then setting .__name__ on function may help, or not; again, I did not test this code, and I don't remember the details of pytest's fixture discovery process.

Upvotes: 1

tmt
tmt

Reputation: 8604

You can parametrize your fixture function which means that each test can give it a a different JSON file to load:

import pytest
import os
import json


@pytest.fixture(scope="class")
def json_content(request):
    base_path = os.path.abspath(..)
    path = os.path.join(base_path, request.param)

    with open(path, "r") as fp:
        data = json.load(fp)

    return data


@pytest.mark.parametrize("json_content", ["abc.json"], indirect=True)
def test_a(json_content):
    print(json_content)


@pytest.mark.parametrize("json_content", ["xyz.json"], indirect=True)
def test_b(json_content):
    print(json_content)

Note: The test examples don't show use of the same fixture per class but that's not necessary to demonstrate the parametrization.

Upvotes: 3

Related Questions