testing-python
Skill0 starsChanged 4 months agoMITView on GitHub
Write and evaluate effective Python tests using pytest. Use when writing tests, reviewing test code, debugging test failures, or improving test coverage. Covers test design, fixtures, parameterization, mocking, and async testing.
2 other projects use a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- TheBeardedBearSAS/claude-crafttheir own version, first seen Oct 29, 2026
- JiangXiu11200/claude-skillsedited, first seen Jul 18, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 1
- JiangXiu11200/claude-skills★ 028% same
Wrote their own version · 1
- TheBeardedBearSAS/claude-craft★ 108same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- apple-mail-mcpb/CLAUDE.mdCLAUDE.md
- CLAUDE.mdCLAUDE.md
- .claude-plugin/CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- tasks/CLAUDE.mdCLAUDE.md
- tests/CLAUDE.mdCLAUDE.md
- tools/CLAUDE.mdCLAUDE.md
- apple-mail-archive-exportSkill
- context-guidance-auditSkill
- cowork-plugin-customizerSkill
- create-cowork-pluginSkill
- finalize-apple-mail-mcpSkill
- find-skillsSkill
- mail-scripting-dictionarySkill
- marketplace-release-handoffSkill
- mcp-builderSkill
- plugin-creatorSkill
- plugin-settingsSkill
- plugin-structureSkill
- python-performance-optimizationSkill
- reviewing-codeSkill
- thermo-nuclear-code-quality-reviewSkill
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 6 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 10
Every test should be atomic, self-contained, and test single functionality.
- Distinctive: line 52
Don't parameterize unrelated behaviors.
- Distinctive: line 77
Put ALL imports at the top of the file:
- Very specific: line 115
Use sender@example.com and invented subjects.
- Distinctive: line 129
Do not scatter counts through prose docs.
- Distinctive: line 170
Test your code with real implementations when possible.
- Distinctive: line 174
Use descriptive names that explain the scenario:
- Very specific: line 204
Use the repo venv (.venv/, editable install).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Writing Effective Python Tests
Core Principles
Every test should be atomic, self-contained, and test single functionality. A test that tests multiple things is harder to debug and maintain.
Test Structure
Atomic unit tests
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()Use parameterization for variations of the same concept
import pytest
@pytest.mark.parametrize("input,expected", [
("hello", "HELLO"),
("World", "WORLD"),
("", ""),
("123", "123"),
])
def test_uppercase_conversion(input, expected):
assert input.upper() == expectedUse separate tests for different functionality
Don't parameterize unrelated behaviors. If the test logic differs, write separate tests.
Rules for this repo (apple-mail-mcp)
tests/CLAUDE.md is canonical for this suite; read it before adding a
test module. The points below are the ones people most often get wrong.
Async tests use unittest.IsolatedAsyncioTestCase
There is no pytest-asyncio and no asyncio_mode setting in this repo, so a bare
module-level async def test_... is collected and then skipped with a warning — it never runs
its assertions. Subclass unittest.IsolatedAsyncioTestCase instead:
import unittest
class DashboardAccountScopeTests(unittest.IsolatedAsyncioTestCase):
async def test_dashboard_passes_account_through(self):
...anyio is present only as a transitive fastmcp dependency, not as a test plugin.
Imports at module level
Put ALL imports at the top of the file:
# Correct
import unittest
from unittest.mock import patch
from apple_mail_mcp.tools import inbox as inbox_tools
def test_something():
...
# Wrong - no local imports
def test_something():
from apple_mail_mcp.tools import inbox # Don't do thisMock AppleScript at the boundary, not the FastMCP transport
Tests here do not drive the server through a fastmcp.client.Client; they call the tool
functions directly and mock the AppleScript boundary. Two established patterns:
# Capture the generated script: patch subprocess.run and read kwargs["input"]
with patch("subprocess.run", side_effect=capture) as mock_run:
...
# Or patch run_applescript in the module under test
with patch("apple_mail_mcp.tools.inbox.run_applescript", return_value=payload):
...Templates live in tests/cross_cutting/test_modernization_3_1_5.py (_ScriptCapture),
tests/search/test_mail_search_tools.py, and tests/compose/test_compose_tools.py.
Local CI-equivalent gates never launch Mail.app.
Fixtures must be synthetic — this repo is PUBLIC
Use sender@example.com and invented subjects. Never build a fixture by pasting a real
message, header block, Message-ID, or account UUID out of a live run, even when reproducing
a real bug: reduce it to the shape that triggers the bug.
Test count is single-sourced
The collected-test count lives only in tools/expected_test_count.txt. After adding or removing
tests, recount and update that one file — the dev-check/release gate fails on drift and prints
the new number:
PYTEST_ADDOPTS='' .venv/bin/pytest --collect-only testsDo not scatter counts through prose docs.
Fixtures
Prefer function-scoped fixtures
@pytest.fixture
def client():
return Client()
async def test_with_client(client):
result = await client.ping()
assert result is not NoneUse tmp_path for file operations
def test_file_writing(tmp_path):
file = tmp_path / "test.txt"
file.write_text("content")
assert file.read_text() == "content"Mocking
Mock at the boundary
from unittest.mock import patch, AsyncMock
async def test_external_api_call():
with patch("mymodule.external_client.fetch", new_callable=AsyncMock) as mock:
mock.return_value = {"data": "test"}
result = await my_function()
assert result == {"data": "test"}Don't mock what you own
Test your code with real implementations when possible. Mock external services, not internal classes.
Test Naming
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():Error Testing
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()Running Tests
Use the repo venv (.venv/, editable install). There is no uv and no pytest-xdist here, so
uv run and -n auto both fail:
.venv/bin/pytest tests/ # full suite
.venv/bin/pytest tests/ -x # stop on first failure
.venv/bin/pytest tests/cli/test_cli.py # specific file
.venv/bin/pytest -k "test_name" # tests matching pattern
bash tools/gates/dev-check.sh # manifests + module budget + pytest + test-count gateChecklist
Before submitting tests:
- Each test tests one thing
- Async tests subclass
unittest.IsolatedAsyncioTestCase(no bareasync def test_) - Imports at module level
- Descriptive test names
- AppleScript mocked at the boundary; no test launches Mail.app
- Fixtures are synthetic (
sender@example.com) — no real mail data tools/expected_test_count.txtupdated if the collected count changed- Parameterization for variations of same behavior
- Separate tests for different behaviors
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

