测试框架
pytest、unittest、doctest、tox。
30 分钟
测试与 pytest
可靠软件离不开自动化测试。pytest 以简洁断言、fixture 和参数化著称,是 Python 项目事实标准。模块组织见 python-fundamentals;Agent 工具链也需测试,参考 ai-agents-essentials;/roadmaps/python。
学习目标
- 编写 pytest 测试函数与 assert 语句
- 使用 fixture 管理测试数据与临时目录
- 了解 mock、覆盖率与 CI 集成要点
基础测试
# tests/test_stats.py
from pathlib import Path
from lesson_tools.stats import count_chars
def test_count_chars(tmp_path: Path) -> None:
(tmp_path / "a.md").write_text("hello", encoding="utf-8")
(tmp_path / "b.md").write_text("world!", encoding="utf-8")
result = count_chars(tmp_path)
assert result == {"a.md": 5, "b.md": 6}
pytest -v
pytest tests/test_stats.py::test_count_chars
fixture 与参数化
import pytest
@pytest.fixture
def sample_draft(tmp_path: Path) -> Path:
(tmp_path / "pya-testing.md").write_text("# Testing\n", encoding="utf-8")
return tmp_path
@pytest.mark.parametrize("text,expected", [
("", 0),
("abc", 3),
("中文", 2),
])
def test_len_text(text: str, expected: int) -> None:
assert len(text) == expected
mock 外部依赖
from unittest.mock import AsyncMock, patch
import asyncio
async def fetch_remote(slug: str) -> str:
... # 实际会请求 HTTP
@pytest.mark.asyncio
async def test_fetch_remote() -> None:
with patch("mymodule.fetch_remote", new_callable=AsyncMock) as mock:
mock.return_value = "cached content"
result = await fetch_remote("pya-testing")
assert "content" in result
安装 pytest-asyncio 以测试 async 代码。
CI 清单
- PR 必跑
pytest、ruff check、mypy - 覆盖率目标:核心模块 ≥ 80%(非硬性,视项目而定)
- 测试应快、独立、可重复
练习
- 为 packaging 课的
count_chars补一个空目录边界测试 - 用
@pytest.mark.parametrize测试 slugify 的 3 组输入 - 在 GitHub Actions 或本地脚本中串联 pytest + ruff