Skip to content

Commit

Permalink
plugin: add mh_fixture
Browse files Browse the repository at this point in the history
It is not possible to access topology fixtures from pytest fixture.
This adds a wrapper to workaround that.

```
@mh_fixture()
def my_fixture(client: Client, request: pytest.FixtureRequest):
    pass

@pytest.mark.topology(KnownTopology.LDAP)
def test_example(client: Client, ldap: LDAP, my_fixture):
    pass
```
  • Loading branch information
pbrezina committed May 30, 2024
1 parent ba7744f commit 15f17af
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 2 deletions.
3 changes: 2 additions & 1 deletion pytest_mh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
mh_utility_postpone_setup,
mh_utility_used,
)
from ._private.plugin import MultihostPlugin, pytest_addoption, pytest_configure
from ._private.plugin import MultihostPlugin, mh_fixture, pytest_addoption, pytest_configure
from ._private.topology import Topology, TopologyDomain
from ._private.topology_controller import TopologyController

Expand All @@ -46,6 +46,7 @@
"mh_utility_postpone_setup",
"mh_utility_ignore_use",
"mh_utility_used",
"mh_fixture",
"pytest_addoption",
"pytest_configure",
"Topology",
Expand Down
70 changes: 69 additions & 1 deletion pytest_mh/_private/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import logging
import sys
import textwrap
from functools import partial, wraps
from os import _exit
from pathlib import Path
from signal import SIGINT, signal
from typing import Generator, Type
from typing import Generator, Literal, Type, get_type_hints

import pytest
import yaml
Expand All @@ -22,6 +23,7 @@
MultihostConfig,
MultihostHost,
MultihostReentrantUtility,
MultihostRole,
mh_utility_enter_dependencies,
mh_utility_exit_dependencies,
mh_utility_setup_dependencies,
Expand Down Expand Up @@ -819,3 +821,69 @@ def pytest_configure(config: pytest.Config):
)

config.pluginmanager.register(MultihostPlugin(config), "MultihostPlugin")


def mh_fixture(scope: Literal["function"] = "function"):
"""
This creates a function-scoped pytest fixture that can access MultihostRole
objects that are available to the test directly.
.. note::
For this to work correctly, all multihost fixtures have to be correctly
typed. It will not work without the type hints.
At this moment, only ``function`` scope is supported.
.. code-block:: python
@mh_fixture()
def my_fixture(client: Client, request: pytest.FixtureRequest):
pass
@pytest.mark.topology(KnownTopology.LDAP)
def test_example(client: Client, ldap: LDAP, my_fixture):
pass
:param scope: Fixture scope, defaults to "function"
:type scope: Literal["function"], optional
"""
def decorator(fn):
full_sig = inspect.signature(fn)
mh_args = []
for arg, hint in get_type_hints(fn).items():
if issubclass(hint, MultihostRole):
mh_args.append(arg)
continue

@wraps(fn)
def wrapper(mh: MultihostFixture, *args, **kwargs):
if "mh" in full_sig.parameters:
kwargs["mh"] = mh

for arg in mh_args:
if arg not in mh.fixtures:
raise KeyError(f"{fn.__name__}: Parameter {arg} is not a valid topology fixture")

kwargs[arg] = mh.fixtures[arg]

return fn(*args, **kwargs)

# Bound multihost parameters
cb = wraps(fn)(partial(wrapper, **{arg: None for arg in mh_args}))

# Create pytest fixture
fixture = pytest.fixture(scope="function")(cb)

# Mock parameters so they are correctly recognized by pytest fixture
partial_parameters = [inspect.Parameter("mh", inspect._POSITIONAL_OR_KEYWORD)]
partial_parameters.extend(
[param for key, param in full_sig.parameters.items() if key != "mh" and key not in mh_args]
)
fixture.__pytest_wrapped__.obj.func.__signature__ = inspect.Signature(
partial_parameters, return_annotation=full_sig.return_annotation
)

return fixture

return decorator

0 comments on commit 15f17af

Please sign in to comment.