An opionated starting point for Python test projects built on pytest. It gives you a working project structure, a configured logger, session-scoped config, automatic fixture discovery, and a few useful hooks out of the box so you can focus on writing tests rather than plumbing.
├── core/ # Framework internals (hooks, config, logger)
├── lib/ # Application-specific test libraries
├── tests/ # Test suites and fixtures
│ ├── config/ # YAML config files loaded automatically at session start
│ ├── fixtures/ # Shared pytest fixtures, discovered automatically
│ └── smoke/ # Example smoke test suite
├── scripts/ # Helper scripts (setup, CI utilities, etc.)
├── results/ # Test run output, ignored by git
├── pyproject.toml # Project metadata, dependencies, and tool config
└── .gitignore
1. Clone the repo
git clone <your-repo-url>
cd pytest-headstart-template2. Install dependencies
Using pip:
pip install -e ".[dev]"Using uv:
uv sync3. Run the tests
pytest # run everything
pytest tests/smoke/ # run a specific suite
pytest -m smoke # run by marker
pytest -m "not smoke" # exclude by markerAt session start, core/config.py builds a config dictionary from base directory paths and any YAML files found under tests/config/. The directory structure becomes the key hierarchy:
tests/config/api/settings.yaml → config["API"]["SETTINGS"]
Access the config in any test via the config fixture:
def test_something(config):
url = config["API"]["SETTINGS"]["base_url"]Every module in core/ creates its own named logger:
from core.logger import get_logger
logger = get_logger(__name__)Test modules use the shared logger for convenience:
from core.logger import loggerBoth approaches write to the console and to results/<run>/run.log. The logger has three extra levels beyond the standard ones: SETUP, TEARDOWN, and SUCCESS.
Any .py file placed under tests/fixtures/ is loaded automatically. No manual imports or registration needed. See tests/README.md for details.
core/hooks.py provides:
@pytest.mark.maxfail(n)— stops running further parametrize variants of a test afternfailurespytest_itemcollected— rewrites node IDs usingTEST_NAMEandTEST_MODULE_NAMEattributes when presentpytest_runtest_logstart— logs the start of every test
See core/README.md for full details on each hook.
Each run writes its output to results/<timestamp>/:
results/
└── 2024-01-15_10-30-00/
├── run.log # full log output
├── report.html # HTML report (requires pytest-html)
└── report.json # JSON report (requires pytest-json-report)
└── latest/
A results/latest/ symlink always points to the most recent run.
- Python 3.12+
- pytest 9.0+
Optional reporting plugins: pytest-html, pytest-json-report. Optional parallel execution: pytest-xdist.