Generate a strict, well-commented pytest setup — config, conftest.py,
markers, and starter tests — and drop it into any project in one command.
Every Python project reinvents the same pytest boilerplate: the addopts line
you always mean to make strict, the four markers you register slightly
differently each time, the conftest.py fixture that chdirs into a temp
directory, the auto-skip hook for slow tests. pytest-starter-config writes all
of it for you — commented, opinionated, and safe to run against an existing
repo (it merges into your pyproject.toml instead of overwriting it).
$ python -m pytest_starter_config --preset full --target . --package myapp --yes
pytest-starter-config 0.1.0: preset=full format=pyproject features=[core, markers, coverage, parallel, randomize, conftest, starter]
Writing into /home/you/myapp:
merge pyproject.toml
kept your existing keys (not overwritten): addopts
create conftest.py
create tests/test_starter.py
The generated config references these plugins:
pip install pytest pytest-cov pytest-xdist pytest-randomly
Prefer to look before you leap? --dry-run prints a real unified diff and
writes nothing:
$ python -m pytest_starter_config --preset standard --dry-run --yes
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,34 @@
+[tool.pytest.ini_options]
+# Strict, fail-fast pytest defaults for a trustworthy suite.
+# see: https://python-testing-debugging.com/.../pytest-configuration-best-practices/
+minversion = "7.0"
+addopts = "-ra --strict-markers --strict-config --cov=src ..."
...
Good pytest configuration is mostly a set of defaults you wish every project
started with: treat warnings as errors so deprecations fail CI instead of
production; --strict-markers and --strict-config so a typo in a marker or a
config key is a hard error, not a silent no-op; xfail_strict so an xfail that
starts passing tells you; registered markers with a hook that keeps slow and
integration tests out of the default run. None of it is hard — it is just
tedious to reassemble, and easy to get subtly wrong, on every new repo.
This tool encodes those defaults as composable features and assembles them. The generated blocks are commented so the config teaches as much as it configures; if you want the reasoning behind a particular default, each block links to the relevant write-up — for example, more on why unregistered markers should be a hard error.
pytest-starter-config is a self-contained, standard-library-only Python
package. It has no third-party dependencies — it only writes files. (The
config it emits references plugins like pytest-cov, pytest-xdist, and
pytest-randomly; install those in the project you generate into, not here.)
Run it from a clone of the source:
git clone https://github.com/python-testing-debugging/pytest-starter-config.git
cd pytest-starter-config
python -m pytest_starter_config --helpInteractive mode (a short menu) runs when you launch it in a terminal without
--yes; add --yes (or pipe input) for non-interactive/CI use.
| Preset | Features |
|---|---|
minimal |
core, markers |
standard |
core, markers, coverage, conftest, starter |
full |
core, markers, coverage, parallel, randomize, conftest, starter |
| Feature | What it contributes |
|---|---|
core |
Strict [tool.pytest.ini_options] defaults (always on). |
markers |
Registered slow/integration/unit/smoke markers + auto-skip hook. |
coverage |
--cov in addopts plus [tool.coverage.run]/[report]. |
parallel |
A note on opting into pytest-xdist (-n auto). |
randomize |
A note on pytest-randomly and reproducing a failing order by seed. |
conftest |
conftest.py fixtures: tmp_workspace, deterministic seed, a factory. |
starter |
tests/test_starter.py demonstrating markers + parametrization. |
| Flag | Purpose |
|---|---|
--preset {minimal,standard,full} |
Starting feature set (default: standard). |
--with FEATURE |
Turn a feature on (repeatable). |
--without FEATURE |
Turn a feature off (repeatable). |
--target DIR |
Directory to generate into (default: current directory). |
--format {pyproject,ini} |
Config in pyproject.toml or a standalone pytest.ini. |
--package NAME |
Import package coverage should measure (default: src). |
--dry-run |
Print a unified diff of what would change; write nothing. |
--force |
Overwrite existing conftest.py / test / pytest.ini files. |
-y, --yes |
Non-interactive: accept the preset/flags without prompting. |
--list-features |
Print available presets and features, then exit. |
- Resolve the preset plus
--with/--withoutinto a final feature set, pulling in any required features (starterneedsmarkersandconftest) and keeping mandatory ones (core). - Render each feature's contributions: keys for the pytest section,
addoptsfragments, extra TOML sections (coverage),conftest.pyblocks, and the starter test file. The__PACKAGE__placeholder is filled from--package. - Plan the writes. For
pyprojectformat, if apyproject.tomlalready exists the pytest section is merged key-by-key — your existing keys win (so re-running never overwrites your settings and is idempotent), and only missing keys are added. Coverage sections are added only if absent. Other files are never overwritten unless--force. - Apply — or, for
--dry-run, print adifflibunified diff and stop. If an existingpyproject.tomlis too irregular to merge safely, the tool falls back to writing a standalonepytest.iniand tells you.
The generated conftest.py wires --run-slow / --run-integration options to
the slow / integration markers, so pytest stays fast by default and
pytest --run-slow opts in.
| Code | Meaning |
|---|---|
| 0 | Files generated (or dry-run diff printed) successfully. |
| 2 | Bad input — unknown feature or preset name. |
Preview and then generate a full setup into a throwaway directory:
python -m pytest_starter_config --preset full --target ./demo-out --yesOr run the bundled script, which does a dry-run preview followed by a real generation into a temp directory and prints the result:
./examples/demo.shThe defaults here come from the pytest configuration, fixture, and marker material at python-testing-debugging.com. For the reasoning behind each generated block: