patha454
patha454

Reputation: 1

How to create dynamic PyTest fixtures from a plugin?

I need to create a PyTest plugin, which can dynamically create fixtures (and add them to existing tests).

Specifically I have a use case to support some legacy test setup and teardown hooks from a PyTest plugin.

I'm attempting to create a PyTest hook which 1) dynamically creates a new fixture, 2) injects the fixture into the test module, and 3) makes relevant test cases use the fixture.

I'm awear of the pytest_runtest_setup and pytest_runtest_teardown hooks, for running code before and after tests - however I also need to support module scope fixtures.

I've based my code on a previous PyTest discussion, which I've tested and showen to work when it runs inside the test module - however running from the PyTest plugin results in the test not being discovered.

Minimum reproducible example:

plugin.py

def pytest_collection_modifyitems(items: List[Item]):
    for item in items:
        if not isinstance(item, Function):
            continue
        pytest_function: Function = item.getparent(Function)    
        def _generate_fixture(name: str):
            @pytest.fixture(name=name)
            def _fixture():
                print("Setup...")
                yield
                print("Teardown...")

            return _fixture

        def inject_fixture(name: str, module: ModuleType):
            setattr(module, name, _generate_fixture(name))

        inject_fixture("example_fixture", pytest_function.module)
        pytest_function: Function = item.getparent(Function)
        pytest_function.fixturenames.append("example_fixture")

test_dummy.py

def test_dummy_func():
    print(globals())
    pass

Expected behaviour: The setup and tear-down prints are executed, observed with pytest test_dummy.py -r P

Actual behaviour: The fixture is not discovered:

============================ ERRORS =============================
_______________ ERROR at setup of test_dummy_func _______________
file /local/home/harleyip/workspaces/LambdaWorkerPythonTestDriver/src/LambdaWorkerPythonTestDriver/test_dummy.py, line 3
  def test_dummy_func():
E       fixture 'example_fixture' not found
>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
>       use 'pytest --fixtures [testpath]' for help on them.

Disabling the fixture, and printing globals() from the integration test, shows the "fixture" is in the globals:

----------Captured stdout call--------------
...
'example_fixture': <function pytest_collection_modifyitems.<locals>.generate_fixture.<locals>._fixture at 0x7fc6336daf70>}
...

Upvotes: 0

Views: 174

Answers (1)

patha454
patha454

Reputation: 1

Turns out PyTest makes this simple, but doesn't document it anywhere. Putting the fixture directly the plugin script makes the fixture accessable to the tests:

@pytest.fixture(autouse=True, scope="module")
def legacy_test_module_hooks():
    print("Setup...")
    yield
    print("Teardown...")

Upvotes: 0

Related Questions