使用pytest编写和评估有效的Python测试。适用于编写测试、审查测试代码、调试测试失败或提高测试覆盖率。
写入有效的 Python 测试是一项面向实际任务的技能,主要用于每个测试都应该是原子的,自成一体的, 并测试单一的功能;. 测试多种事物的测试更难调试和维护。
从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;
若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
每个测试都应是原子的、自包含的,且仅测试单一功能。测试多个行为的用例更难调试与维护。
每个测试应验证一种行为。测试名称应在失败时清晰表明哪个行为出错。当多个断言均用于验证同一行为时,允许多个断言共存。
# 优秀:名称明确指示失败原因
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
# 不佳:若此测试失败,无法判断具体是哪项行为异常?
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
切勿对不相关的功能进行参数化。若测试逻辑存在差异,请分别编写独立测试。
本项目全局启用 asyncio_mode = "auto"。请直接编写异步测试函数,无需额外装饰器:
# 正确写法
async def test_async_operation():
result = await some_async_function()
assert result == expected
# 错误写法 —— 请勿添加该装饰器
@pytest.mark.asyncio
async def test_async_operation():
...
所有导入语句必须位于文件顶部:
# 正确写法
import pytest
from fastmcp import FastMCP
from fastmcp.client import Client
async def test_something():
mcp = FastMCP("test")
...
# 错误写法 —— 禁止局部导入
async def test_something():
from fastmcp import FastMCP # 请勿如此操作
...
将 FastMCP 服务实例直接传入客户端:
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!"
仅在显式测试网络相关功能时才使用 HTTP 传输。
对 JSON Schema 及其他复杂结构的测试,请使用 inline-snapshot:
from inline_snapshot import snapshot
def test_schema_generation():
schema = generate_schema(MyModel)
assert schema == snapshot() # 首次运行时自动填充
常用命令:
pytest --inline-snapshot=create — 填充空快照pytest --inline-snapshot=fix — 在有意变更后更新快照@pytest.fixture
def client():
return Client()
async def test_with_client(client):
result = await client.ping()
assert result is not None
tmp_pathdef test_file_writing(tmp_path):
file = tmp_path / "test.txt"
file.write_text("content")
assert file.read_text() == "content"
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"}
尽可能使用真实实现来测试自身代码。仅 Mock 外部服务,而非内部类。
使用具备描述性的名称,准确表达所测试的场景:
# 优秀 def test_login_fails_with_invalid_password(): def test_user_can_update_own_profile(): def test_admin_can_delete_any_user(): # 不佳 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 # 并行运行全部测试 uv run pytest -n auto -x # 首次失败即停止 uv run pytest path/to/test.py # 运行指定文件 uv run pytest -k "test_name" # 运行匹配模式的测试 uv run pytest -m "not integration" # 排除集成测试
提交测试前请确认:
@pytest.mark.asyncio 装饰器