# Python Pytest

> Write and structure pytest tests to this project's conventions. Use when authoring tests, parametrizing cases, handling warnings, deciding test coverage, or laying out the tests directory.

- **Type:** Skill
- **Install:** `agentstack add skill-owenlamont-agent-skills-python-pytest`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [owenlamont](https://agentstack.voostack.com/s/owenlamont)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [owenlamont](https://github.com/owenlamont)
- **Source:** https://github.com/owenlamont/agent_skills/tree/main/skills/python-pytest

## Install

```sh
agentstack add skill-owenlamont-agent-skills-python-pytest
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Python pytest Conventions

Standards for authoring tests and laying out the test suite, which uses
[pytest](https://docs.pytest.org/).

## Writing tests

- Use test **functions** only — never test classes.
- Do most test setup with fixtures and parameters so the code under test is at, or near,
  the beginning of the test function.
- Compose fixtures from other fixtures rather than mutating fixture values, put shared
  fixtures in `conftest.py`, give each fixture a single purpose, and restore any global
  state you mutate after the `yield`.
- Use explicit `pytest.param(...)` instances in parametrized tests, each with a
  meaningful `id`. Param ids can contain spaces and should be written in a human-readable
  way.
- Don't parametrize a test that has only one case just to attach an id — keep it a plain
  test function.
- When a parametrized case needs more than about three values, wrap them in a small
  dataclass passed as one param instead of a long tuple.
- Don't use docstrings or comments in tests. Convey the test's purpose through meaningful
  function names, variable names, and param ids.

```python
import pytest

@pytest.mark.parametrize(
    ("celsius", "expected_fahrenheit"),
    [
        pytest.param(0, 32, id="freezing point of water"),
        pytest.param(100, 212, id="boiling point of water"),
        pytest.param(-40, -40, id="scales cross over"),
    ],
)
def test_celsius_to_fahrenheit(celsius, expected_fahrenheit):
    assert celsius_to_fahrenheit(celsius) == expected_fahrenheit
```

## Test design

- Mock at the boundary of services you don't own — outbound HTTP, third-party
  APIs; use real implementations for all internal code, including your own thin wrappers
  (mock the external call inside the wrapper, not the wrapper itself).
  The database is the exception: use a real instance (see External services).
- For mocking use pytest-mock's `mocker`, not `monkeypatch` or `unittest.mock`.
  Patch the name in the namespace under test: `under_test.fn` when it does
  `from other import fn`, or `other.fn` when it does `import other`; pass
  `autospec=True` (or `spec=`) so signatures stay honest.
- One behaviour per test: assert as many facets of that single result as you need, but
  don't bundle unrelated behaviours into one test.
- Derive cases from the behavioural contract: parametrize over distinct observable
  outcomes, collapse input combinations that behave identically, and assert the whole
  result.
- Don't re-assert what the type system, framework, or DB constraint already guarantees.
- Test through the public API — it's the stable contract, whereas private
  (underscore-prefixed) functions are implementation details you should be free to
  refactor without touching tests. Don't import a private function to test it; cover it
  indirectly through its public callers (preferred), promote it to public if it
  genuinely needs direct testing, or drop the test if it adds no meaningful coverage.
- Don't put control flow (`if`/`for`/`try`) in a test, and don't assert on log output
  or third-party library behaviour — test your code, not its dependencies'.
- Use a raw string for `pytest.raises(match=...)` patterns that contain regex
  metacharacters.
- Make test data deterministic: freeze time (`freezegun`/`time-machine`) for any
  `now()`/expiry logic, and use fixed UUIDs (`uuid.UUID(int=N, version=4)`) — never the
  real clock or random UUIDs.

## External services in tests

How the external boundaries above are mocked (or not) in practice.

### Postgres — a real container via `testcontainers`

- Postgres is **not** mocked. A real, ephemeral Postgres runs via `testcontainers`
  (`PostgresContainer`), session-scoped.
- Consequence: DB-backed (integration) tests require Docker to be running; pure unit
  tests don't.

## Warnings

- Warnings are treated as errors in tests.
- Warnings emitted from code in this repo must be fixed at the source, not silenced.
- Warnings emitted from third-party packages may be ignored, using the most specific
  ignore practical.

## Coverage philosophy

- Aim for high branch coverage, but hold it in tension with test bloat — a goal, not an
  absolute. Every line of test code has a maintenance cost, so don't add tests that don't
  meaningfully exercise behaviour.
- Watch the ratio of test code to source code. Past roughly 2:1 you're usually paying to
  maintain a lot of test code for diminishing returns — exercising ever-smaller slivers
  of source. When closing the last uncovered lines would push the ratio that high, prefer
  leaving them uncovered (with a justified `# pragma: no cover` where apt) over bloating
  the suite.
- Favour higher-level tests near public entry points (e.g. an API endpoint or CLI
  command) over many small unit tests: they cover more code per test, cost less to
  maintain when internals change, and keep the ratio down.
- Run coverage with `--cov-branch`. Cover new branches and error paths by default, but
  don't ratchet toward 100% at any cost: a deliberately uncovered path is fine when a test
  would cost more to maintain than it is worth.
- When a change grows the test suite, sanity-check the ratio with the bundled
  `scripts/source_size.py`: it reports `src/` vs `tests/` size per directory and, with
  `--compare-to `, the delta that change adds.

## Test directory layout

- `/tests/` mirrors the `/src/` directory structure and stays in sync with it.
- Test modules are (in most cases) named after the src module they test, with a `test_`
  prefix. Rarer tests covering functionality spanning several src modules are the
  exception.
- Don't create sub-packages in the test directories — no `__init__.py` files (a
  `conftest.py` is fine). A consequence of this strategy: no duplicate module (`.py`)
  file names are allowed anywhere in the repo, with the exception of `__init__.py` and
  `conftest.py` files, since pytest can't support duplicate test file names without
  sub-packages.

## Running tests

These are reference commands — run them when it suits your workflow:

```bash
# Run a single test file
uv run pytest tests/test_module.py

# Run the whole suite in parallel
uv run pytest -n logical --color=no

# Run an individual test (drop -n logical when running a single test)
uv run pytest tests/test_module.py::test_name
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [owenlamont](https://github.com/owenlamont)
- **Source:** [owenlamont/agent_skills](https://github.com/owenlamont/agent_skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-owenlamont-agent-skills-python-pytest
- Seller: https://agentstack.voostack.com/s/owenlamont
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
