with one click
pytest-conftest
// Organize pytest fixtures, hooks, and configuration across directory hierarchies using conftest.py
// Organize pytest fixtures, hooks, and configuration across directory hierarchies using conftest.py
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | pytest-conftest |
| description | Organize pytest fixtures, hooks, and configuration across directory hierarchies using conftest.py |
| tech_stack | ["pytest"] |
| language | ["python"] |
| capability | ["unit-testing","integration-testing"] |
| version | pytest 9.x |
| collected_at | "2025-07-18T00:00:00.000Z" |
Source: https://docs.pytest.org/en/stable/reference/fixtures.html, https://docs.pytest.org/en/stable/how-to/fixtures.html
conftest.py is pytest's mechanism for sharing fixtures, hooks, and configuration across multiple test modules within a directory ā without any imports. Each directory in a test tree can have its own conftest.py, forming a layered hierarchy where child directories inherit from parents and can selectively override.
tests/unit/ vs tests/integration/)pytest_addoption, pytest_generate_tests, pytest_configure)conftest.pytests/
āāā conftest.py # root: fixtures available everywhere
āāā test_auth.py
āāā unit/
ā āāā conftest.py # unit-only fixtures; can override root fixtures
ā āāā test_core.py
āāā integration/
āāā conftest.py # integration-only fixtures
āāā test_api.py
Fixtures are discovered upward only: a test in tests/unit/test_core.py sees fixtures from tests/unit/conftest.py and tests/conftest.py, but never from tests/integration/conftest.py.
The first fixture found wins ā defining a fixture with the same name in a closer conftest.py silently overrides the parent's version.
# tests/conftest.py
import pytest
import psycopg2
@pytest.fixture(scope="session")
def db():
conn = psycopg2.connect("dbname=test user=postgres")
yield conn
conn.close()
@pytest.fixture
def cursor(db):
cur = db.cursor()
yield cur
cur.close()
# tests/conftest.py
@pytest.fixture
def api_base():
return "https://api.example.com"
# tests/integration/conftest.py
@pytest.fixture
def api_base():
return "http://localhost:8000" # overrides root for integration tests
# tests/conftest.py
def pytest_addoption(parser):
parser.addoption("--env", default="dev", choices=["dev", "staging", "prod"])
@pytest.fixture(scope="session")
def env_config(request):
env = request.config.getoption("--env")
return {"dev": {...}, "staging": {...}, "prod": {...}}[env]
# tests/conftest.py
def pytest_generate_tests(metafunc):
if "backend" in metafunc.fixturenames:
metafunc.parametrize("backend", ["sqlite", "postgresql", "mysql"])
| Item | Role |
|---|---|
@pytest.fixture(scope=..., autouse=...) | Define a fixture; scope = function/class/module/package/session or a callable |
request fixture | Introspect the requesting test: request.config, request.param, request.addfinalizer(fn) |
pytestconfig fixture | Access full pytest config, plugin manager, and all CLI options |
tmp_path_factory fixture | Session-scoped temp-dir factory (use in session-scoped fixtures; tmp_path is function-scoped) |
pytest_addoption(parser) | Hook for adding CLI flags ā works in any conftest.py |
pytest_generate_tests(metafunc) | Hook for dynamic parametrization ā metafunc.parametrize(...) |
pytest_configure(config) / pytest_sessionstart(session) | Lifecycle hooks for setup before any tests run |
session ā package ā module ā class ā function
Higher-scoped fixtures execute first. Within the same scope, dependencies determine order: if fixture A names fixture B as a parameter, B runs first.
conftest.py files.conftest.py are executed at collection time for every test in that directory.conftest.py fixtures always take priority over identically-named plugin fixtures.tests/conftest.py and specialize in subdirectory conftest.py files via override.conftest.py for global resources (DB pools, Docker containers); use function-scoped fixtures for per-test isolation.conftest.py near the project root so all subdirectories inherit them.pytest_generate_tests in a conftest.py rather than decorating every test function with @pytest.mark.parametrize.yield fixtures (not addfinalizer) for teardown ā they're clearer and the standard pattern. Use addfinalizer only when teardown registration must happen conditionally mid-fixture.