Install
$ agentstack add skill-owenlamont-agent-skills-python-pytest ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Python pytest Conventions
Standards for authoring tests and laying out the test suite, which uses pytest.
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.
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, notmonkeypatchorunittest.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__.pyfiles (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:
# 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
- Source: owenlamont/agent_skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.