What this skill does
Generates pytest test files for a Python codebase (or a targeted scope) that:
- Target 80% line coverage as a floor, prioritising public API surface, business logic, and error branches.
- Follow pytest naming conventions (
test_*.pyfiles,test_prefix functions and methods,Testprefix classes). - Use real implementations rather than mocked stubs wherever the real code is importable within the same process.
- Mark tests that require live external services with
@pytest.mark.integration. - Create or update
pyproject.tomlwith the[tool.pytest.ini_options]block using--import-mode=importlib. - Detect project layout (
src/vs flat) and configure accordingly. - Output a Coverage Gap Report after the tests listing what is covered, what is skipped, and why each skip is justified.
When to use it
- User asks to "write tests", "generate pytest tests", "add test coverage", or "test this module/function".
- A new Python project has no
tests/directory and nopyproject.tomlpytest configuration. - A user wants to understand what is and is not covered after a test generation pass.
- User says "get me to 80% coverage" or "what's not tested?".
Do NOT use this skill for:
- Test strategy design — use the
test-strategyskill instead. - Acceptance or UAT test cases derived from user stories.
- TypeScript, JavaScript, or other non-Python projects.
Key concepts
Layout detection
| Layout | Indicator | testpaths | Import mode |
|---|---|---|---|
src/ layout |
src/ directory at root |
["tests"] |
importlib |
| Flat layout | Package at root, no src/ |
["tests"] |
importlib |
| Inline tests | No tests/ dir — tests live next to source |
auto-discovery | importlib |
Always prefer tests outside application code (tests/ at root). Do not create an init file in tests/ — it is not needed under importlib mode and causes import confusion.
Coverage target priority
Test in this order when 80% coverage is the target:
- All public functions and methods — always cover these.
- All error branches and exception paths — at least one failure case per public function.
- Boundary values and edge cases — parametrize with zero, negative, empty, and type-mismatch inputs.
- Private helpers (
_name) — test indirectly through their public callers; mark skipped in the gap report. - CLI entrypoints (
if __name__ == "__main__":) — note as manual test candidates; apply# pragma: no cover. - Tests requiring live databases or network — mark with
@pytest.mark.integration; exclude from the 80% threshold.
What not to generate
setup.py testor anypytest-runnerinvocation — deprecated and removed.- Tests that
mock.patcha real, in-process implementation — use the real class. - Assertion-free tests that always pass regardless of the implementation.
- An init file inside
tests/.
Instructions
Step 1 — Determine scope
Read the user's request:
- No scope given — whole codebase. Walk the source tree and identify all Python modules.
- File given — test that file only; still produce a gap report for it.
- Module given — test the whole module.
- Function given — write focused tests for that function; note what is out of scope.
For a large codebase, list the files to be created and ask for confirmation before generating.
Step 2 — Detect layout
Check for the presence of:
- A
src/directory at the project root →src/layout. - A package directory at root (contains an init file) → flat layout.
- An existing
tests/directory → confirm placement; use it. - An existing
pyproject.toml→ read[tool.pytest.ini_options]before updating.
Step 3 — Generate or update pyproject.toml
If pyproject.toml does not exist, create it. If it exists but has no [tool.pytest.ini_options] section, add only that section. Never overwrite an existing [project] block.
Required minimum:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "PACKAGENAME"
version = "PACKAGEVERSION"
[tool.pytest.ini_options]
addopts = ["--import-mode=importlib"]
testpaths = ["tests"]
markers = [
"integration: tests that require live external services (deselect with '-m not integration')",
]
Replace PACKAGENAME with the detected package name and PACKAGEVERSION with "0.1.0" if none exists.
Step 4 — Generate test files
For each module in scope:
- Read the source file to identify all public classes, methods, and functions.
- Write a
tests/test_<module>.pyfile. - Cover the happy path for every public function.
- Cover at least one error path or edge case per function.
- Mark tests that call live external services with
@pytest.mark.integration. - For private functions (
_name), do NOT write direct tests — test them indirectly through their callers. - Use
@pytest.mark.parametrizewhen multiple inputs test the same logic. - Use
pytest.raises(ExceptionType, match="pattern")for all exception cases — never omitmatch=.
Enforce these naming rules:
- Files:
test_<module>.py(preferred) or<module>_test.py - Functions:
test_<what_it_tests> - Classes:
TestClassName(PascalCase,Testprefix, no init method) - No init file in
tests/
Step 5 — Output the Coverage Gap Report
After the test files, always output a ## Coverage Gap Report section:
## Coverage Gap Report
### Covered
| Module | Items tested | Test file |
| --- | --- | --- |
| `src/mypkg/app.py` | `create_app`, all routes, 404 handler | `tests/test_app.py` |
| `src/mypkg/models.py` | `Item` init, `to_dict`, `from_dict` (valid + invalid), `save` | `tests/test_models.py` |
### Skipped
| Module | Item | Reason |
| --- | --- | --- |
| `src/mypkg/app.py` | `_configure_logging()` | Private helper — exercised indirectly via every `create_app()` call |
| `src/mypkg/app.py` | `if __name__ == "__main__":` | CLI entrypoint — `# pragma: no cover` applied |
### Estimated coverage
| Module | Estimated line coverage | At 80% target? |
| --- | --- | --- |
| `app.py` | ~87% | Yes |
| `models.py` | ~93% | Yes |
Output format
For each run the skill produces:
pyproject.toml— created or updated with[tool.pytest.ini_options].- Test files — one per module (or one per targeted scope), with full pytest-compliant content.
- Coverage Gap Report — inline in the response, in the format above.
When generating for a whole codebase, list all files to be created before generating them.
Examples
Example 1 — Whole codebase, no prior tests
Input: "Write pytest tests for my Flask API project."
Steps the skill takes:
- Detects
src/flaskapi/layout. - Creates
pyproject.tomlwith hatchling backend and--import-mode=importlib. - Creates
tests/test_app.pycovering all public routes (happy path, 404, validation errors). - Creates
tests/test_models.pycovering allItemmethods includingsave. - Marks any tests calling real external services with
@pytest.mark.integration. - Outputs a Coverage Gap Report noting
_configure_loggingskipped as a private helper.
Example 2 — Scoped to a single function
Input: "Write tests for the parse_date function in utils.py."
Steps the skill takes:
- Writes
tests/test_utils.pycovering: valid ISO date string, invalid format raisesValueError, empty string raisesValueError, timezone-aware input, leap year boundary. - Coverage Gap Report notes all other functions in
utils.pyare out of scope.
Example 3 — Existing pyproject.toml, missing pytest config
Input: "Add pytest config — I already have a pyproject.toml."
Steps the skill takes:
- Reads existing
pyproject.toml. - Merges only the
[tool.pytest.ini_options]block withaddopts = ["--import-mode=importlib"]. - Does not overwrite
[project],[build-system], or any other existing section. - Confirms the merged result.
Notes
- Always use
--import-mode=importlib. It avoidssys.pathmanipulation and will become the default in a future pytest major version. Seereferences/test-layout.md. - No init file in
tests/. Underimportlibmode it is not needed, and adding one causes import confusion. - When the real implementation is available and in-process, do not mock it. Mocking real classes tests the mock, not the system.
@pytest.mark.integrationtests go in the same test files as unit tests but are excluded from the default run with-m "not integration". Declare the marker in[tool.pytest.ini_options].- This skill pairs with
test-strategy(define what to test before generating) andcode-review(review generated tests for correctness and quality). - Reference files:
references/project-setup.md,references/test-layout.md,references/pytest-config.md,references/discovery-conventions.md,references/coverage-and-quality.md.