Skip to content

Instantly share code, notes, and snippets.

@LewisGaul
Last active October 29, 2022 18:17
Show Gist options
  • Save LewisGaul/126049eec2911a5d557285cff0e3799e to your computer and use it in GitHub Desktop.
Save LewisGaul/126049eec2911a5d557285cff0e3799e to your computer and use it in GitHub Desktop.
Pytest setup that runs first during fixture setup stage
__all__ = ()
import logging
from typing import Set
import pytest
logger = logging.getLogger(__name__)
def pytest_configure(config: pytest.Config) -> None:
config.addinivalue_line(
"markers",
"require_features(FEAT, ...): specify required host features",
)
@pytest.fixture(scope="session")
def host(pytestconfig: pytest.Config) -> str:
if address := pytestconfig.getoption("host_address", None):
logger.info("Using host address %s", address)
return address
else:
logger.info("Using localhost")
return "localhost"
@pytest.fixture(scope="session", autouse=True)
def _skip_marked_tests_as_applicable(request: pytest.FixtureRequest, host: str) -> None:
session = request.node
host_features = _detect_host_features(host)
for item in session.items:
if mark := item.get_closest_marker("require_features"):
required_features = set(mark.args)
else:
required_features = set()
missing_features = required_features - host_features
if missing_features:
item.add_marker(
pytest.mark.skip(
f"Host missing features: " + ",".join(sorted(missing_features))
)
)
def _detect_host_features(host: str) -> Set[str]:
# Run commands on the host over SSH to determine what features it provides.
...
return {"bar", "baz"}
$PYTEST_PLUGINS=tests.plugin pytest tests/ --log-cli-level info --setup-vm
=============================================================== test session starts ===============================================================
platform linux -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
rootdir: pytest-playground, configfile: pytest.ini
collected 3 items
tests/test_1.py::test_A
----------------------------------------------------------------- live log setup ------------------------------------------------------------------
INFO tests.plugin:plugin.py:38 Booting VM...
INFO tests.conftest:conftest.py:22 Using VM host 10.0.0.123
------------------------------------------------------------------ live log call ------------------------------------------------------------------
INFO tests.test_1:test_1.py:10 Running test A
PASSED [ 33%]
tests/test_1.py::test_B SKIPPED (Host missing features: foo) [ 66%]
tests/test_1.py::test_C
------------------------------------------------------------------ live log call ------------------------------------------------------------------
INFO tests.test_1:test_1.py:20 Running test C
PASSED [100%]
---------------------------------------------------------------- live log teardown ----------------------------------------------------------------
INFO tests.plugin:plugin.py:43 Tearing down the VM...
========================================================== 2 passed, 1 skipped in 0.03s ===========================================================
import logging
from typing import List
import pytest
logger = logging.getLogger(__name__)
def pytest_addoption(parser: pytest.Parser) -> None:
group = parser.getgroup("vm_opts", "VM plugin opts")
group.addoption(
"--setup-vm",
action="store_true",
help="Enable setting up a VM",
)
def pytest_configure(config: pytest.Config) -> None:
if config.option.setup_vm:
config.option.host_address = "10.0.0.123"
def pytest_collection_modifyitems(
config: pytest.Config, items: List[pytest.Item]
) -> None:
# Force setup fixture to run first.
# Added as a dependency for all tests (effectively like autouse).
if config.option.setup_vm:
for item in items:
item.fixturenames.insert(0, "vm_setup")
@pytest.fixture(scope="session")
def vm_setup() -> None:
_boot_vm()
yield
_teardown_vm()
def _boot_vm():
logger.info("Booting VM...")
...
def _teardown_vm():
logger.info("Tearing down the VM...")
...
import logging
import pytest
logger = logging.getLogger(__name__)
def test_A(host):
logger.info("Running test A")
@pytest.mark.require_features("foo")
def test_B(host):
logger.info("Running test B")
@pytest.mark.require_features("bar")
def test_C(host):
logger.info("Running test C")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment