From squid
Writes and evaluates Python tests using pytest. Use when writing tests, reviewing test code, debugging test failures, or improving test coverage.
How this skill is triggered — by the user, by Claude, or both
Slash command
/squid:squid-testing-pythonThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Every test should be **atomic**, **self-contained**, and test **single functionality**. A test that tests multiple things is harder to debug and maintain.
Every test should be atomic, self-contained, and test single functionality. A test that tests multiple things is harder to debug and maintain.
Keep a one-to-one relationship between test files and the modules they cover: myapp/service.py → tests/.../test_service.py. This makes the test for any given module obvious and keeps coverage gaps visible.
Structure each test body in three beats — set up inputs (Arrange), call the thing under test (Act), then assert on the result. Keep them in that order; don't interleave more setup after the act.
Each test should verify a single behavior. The test name should tell you what's broken when it fails. Multiple assertions are fine when they all verify the same behavior.
# Good: Name tells you what's broken
def test_user_creation_sets_defaults():
user = User(name="Alice")
assert user.role == "member"
assert user.id is not None
assert user.created_at is not None
# Bad: If this fails, what behavior is broken?
def test_user():
user = User(name="Alice")
assert user.role == "member"
user.promote()
assert user.role == "admin"
assert user.can_delete_others()
import pytest
@pytest.mark.parametrize("input,expected", [
("hello", "HELLO"),
("World", "WORLD"),
("", ""),
("123", "123"),
])
def test_uppercase_conversion(input, expected):
assert input.upper() == expected
Don't parameterize unrelated behaviors. If the test logic differs, write separate tests.
Put ALL imports at the top of the file. Do not import inside test function bodies.
# Correct
import pytest
from myapp.service import do_work
def test_something():
assert do_work() is not None
# Wrong - no local imports
def test_something():
from myapp.service import do_work # Don't do this
...
If the project sets asyncio_mode = "auto" in pyproject.toml, write async tests without decorators:
# Correct (when asyncio_mode = "auto")
async def test_async_operation():
result = await some_async_function()
assert result == expected
Otherwise, mark explicitly with @pytest.mark.asyncio.
If the project uses inline-snapshot, use it for JSON schemas and complex structures:
from inline_snapshot import snapshot
def test_schema_generation():
schema = generate_schema(MyModel)
assert schema == snapshot() # Will auto-populate on first run
Commands:
pytest --inline-snapshot=create - populate empty snapshotspytest --inline-snapshot=fix - update after intentional changesPut shared fixtures in conftest.py so they're available across test files without imports. Use fixtures (and their yield-based teardown) for setup and cleanup — avoid xUnit-style setUp/tearDown methods.
@pytest.fixture
def client():
return Client()
def test_with_client(client):
result = client.ping()
assert result is not None
tmp_path for file operationsdef test_file_writing(tmp_path):
file = tmp_path / "test.txt"
file.write_text("content")
assert file.read_text() == "content"
Use pytest-mock's mocker fixture (preferred) or unittest.mock patches.
from unittest.mock import AsyncMock
async def test_external_api_call(mocker):
mock = mocker.patch("mymodule.external_client.fetch", new_callable=AsyncMock)
mock.return_value = {"data": "test"}
result = await my_function()
assert result == {"data": "test"}
Test your code with real implementations when possible. Mock external services (HTTP APIs, third-party SDKs), not your own internal classes.
Orchestrators, model-serving runtimes, observability clients, and similar infrastructure should be exercised via integration tests, not unit tests with mocks of their internals.
Test files must be named test_*.py and test functions test_* so pytest discovers them. Beyond that, use descriptive names that explain the scenario:
# Good
def test_login_fails_with_invalid_password():
def test_user_can_update_own_profile():
def test_admin_can_delete_any_user():
# Bad
def test_login():
def test_update():
def test_delete():
import pytest
def test_raises_on_invalid_input():
with pytest.raises(ValueError, match="must be positive"):
calculate(-1)
async def test_async_raises():
with pytest.raises(ConnectionError):
await connect_to_invalid_host()
uv run pytest -n auto # Run all tests in parallel
uv run pytest -n auto -x # Stop on first failure
uv run pytest path/to/test.py # Run specific file
uv run pytest -k "test_name" # Run tests matching pattern
uv run pytest -m "not integration" # Exclude integration tests
Prefer project Make targets when available: make unit-tests, make integration-tests, make tests.
Before submitting tests:
npx claudepluginhub iusztinpaul/squid --plugin squidProvides pytest testing patterns for Python projects: test structure, fixtures, parametrize, conftest.py, mocking with unittest.mock and monkeypatch, and coverage with pytest-cov.
Guides Python testing with pytest: TDD cycle, fixture patterns, mocking, parametrization, and 80%+ coverage targets. Activates when writing Python tests or setting up coverage infrastructure.
pytest testing framework conventions and practices. Invoke whenever task involves any interaction with pytest — writing tests, configuring pytest, fixtures, parametrize, mocking, debugging test failures, or coverage.