testing-python

安装量: 63
排名: #11964

安装

npx skills add https://github.com/jlowin/fastmcp --skill testing-python

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() == expected

Use separate tests for different functionality

Don't parameterize unrelated behaviors. If the test logic differs, write separate tests.

Project-Specific Rules No async markers needed

This project uses asyncio_mode = "auto" globally. Write async tests without decorators:

Correct

async def test_async_operation(): result = await some_async_function() assert result == expected

Wrong - don't add this

@pytest.mark.asyncio async def test_async_operation(): ...

Imports at module level

Put ALL imports at the top of the file:

Correct

import pytest from fastmcp import FastMCP from fastmcp.client import Client

async def test_something(): mcp = FastMCP("test") ...

Wrong - no local imports

async def test_something(): from fastmcp import FastMCP # Don't do this ...

Use in-memory transport for testing

Pass FastMCP servers directly to clients:

from fastmcp import FastMCP from fastmcp.client import Client

mcp = FastMCP("TestServer")

@mcp.tool def greet(name: str) -> str: return f"Hello, {name}!"

async def test_greet_tool(): async with Client(mcp) as client: result = await client.call_tool("greet", {"name": "World"}) assert result[0].text == "Hello, World!"

Only use HTTP transport when explicitly testing network features.

Inline snapshots for complex data

Use inline-snapshot for testing 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 snapshots pytest --inline-snapshot=fix - update after intentional changes 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 None

Use 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 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

Checklist

Before submitting tests:

Each test tests one thing No @pytest.mark.asyncio decorators Imports at module level Descriptive test names Using in-memory transport (not HTTP) unless testing networking Parameterization for variations of same behavior Separate tests for different behaviors

返回排行榜