mirror of
https://github.com/luongnv89/claude-howto.git
synced 2026-05-23 10:19:41 +02:00
89e89d4aa3
Add Chinese (Simplified) translations for all documentation, organized under a dedicated zh/ directory that mirrors the English folder structure. Co-authored-by: tanqingkuang <tanqingkuang@users.noreply.github.com> Translations originally contributed by @tanqingkuang in #45. Restructured from *-CN.md suffix pattern into zh/ directory to prevent the EPUB builder (scripts/build_epub.py collect_folder_files) from picking up Chinese files via glob("*.md") inside module folders.
7.9 KiB
7.9 KiB
测试指南
本文档说明 Claude How To 的测试基础设施。
概览
项目使用 GitHub Actions 在每次 push 和 pull request 时自动运行测试。测试覆盖:
- 单元测试:使用 pytest 的 Python 测试
- 代码质量:使用 Ruff 做 lint 和格式化
- 安全:使用 Bandit 做漏洞扫描
- 类型检查:使用 mypy 做静态类型分析
- 构建验证:EPUB 生成测试
在本地运行测试
前置条件
# 安装 uv(快速 Python 包管理器)
pip install uv
# 或者在 macOS 上使用 Homebrew
brew install uv
配置环境
# 克隆仓库
git clone https://github.com/luongnv89/claude-howto.git
cd claude-howto
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate # macOS/Linux
# 或者
.venv\Scripts\activate # Windows
# 安装开发依赖
uv pip install -r requirements-dev.txt
运行测试
# 运行所有单元测试
pytest scripts/tests/ -v
# 运行带覆盖率的测试
pytest scripts/tests/ -v --cov=scripts --cov-report=html
# 运行指定测试文件
pytest scripts/tests/test_build_epub.py -v
# 运行指定测试函数
pytest scripts/tests/test_build_epub.py::test_function_name -v
# 以 watch 模式运行测试(需要 pytest-watch)
ptw scripts/tests/
运行 lint
# 检查代码格式
ruff format --check scripts/
# 自动修复格式问题
ruff format scripts/
# 运行 lint
ruff check scripts/
# 自动修复 lint 问题
ruff check --fix scripts/
运行安全扫描
# 运行 Bandit 安全扫描
bandit -c pyproject.toml -r scripts/ --exclude scripts/tests/
# 生成 JSON 报告
bandit -c pyproject.toml -r scripts/ --exclude scripts/tests/ -f json -o bandit-report.json
运行类型检查
# 使用 mypy 检查类型
mypy scripts/ --ignore-missing-imports --no-implicit-optional
GitHub Actions 工作流
触发条件
- 推送到
main或develop分支(当scripts有变更时) - 向
main提交 Pull Request(当scripts有变更时) - 手动触发 workflow
作业
1. 单元测试(pytest)
- 运行环境:Ubuntu latest
- Python 版本:3.10、3.11、3.12
- 执行内容:
- 从
requirements-dev.txt安装依赖 - 运行 pytest 并生成覆盖率报告
- 将覆盖率上传到 Codecov
- 归档测试结果和 HTML 覆盖率报告
- 从
结果:如果任何测试失败,工作流失败(关键)
2. 代码质量(Ruff)
- 运行环境:Ubuntu latest
- Python 版本:3.11
- 执行内容:
- 使用
ruff format检查格式 - 使用
ruff check运行 lint - 报告问题,但不会让整个工作流失败
- 使用
结果:非阻塞(仅警告)
3. 安全扫描(Bandit)
- 运行环境:Ubuntu latest
- Python 版本:3.11
- 执行内容:
- 扫描安全漏洞
- 生成 JSON 报告
- 将报告作为 artifact 上传
结果:非阻塞(仅警告)
4. 类型检查(mypy)
- 运行环境:Ubuntu latest
- Python 版本:3.11
- 执行内容:
- 执行静态类型分析
- 报告类型不匹配
- 帮助尽早发现 bug
结果:非阻塞(仅警告)
5. 构建 EPUB
- 运行环境:Ubuntu latest
- 依赖:pytest、lint、安全扫描(都必须通过)
- 执行内容:
- 使用
scripts/build_epub.py构建 EPUB - 验证 EPUB 是否成功生成
- 将 EPUB 作为 artifact 上传
- 使用
结果:如果构建失败,工作流失败(关键)
6. 总结
- 运行环境:Ubuntu latest
- 依赖:所有其他作业
- 执行内容:
- 生成工作流总结
- 列出所有 artifacts
- 汇总总体状态
编写测试
测试结构
测试应放在 scripts/tests/ 中,文件名形如 test_*.py:
# scripts/tests/test_example.py
import pytest
from scripts.example_module import some_function
def test_basic_functionality():
"""测试 some_function 是否正常工作。"""
result = some_function("input")
assert result == "expected_output"
def test_error_handling():
"""测试 some_function 是否能优雅处理错误。"""
with pytest.raises(ValueError):
some_function("invalid_input")
@pytest.mark.asyncio
async def test_async_function():
"""测试异步函数。"""
result = await async_function()
assert result is not None
测试最佳实践
- 使用有描述性的名称:例如
test_function_returns_correct_value() - 尽量每个测试只做一个断言:更容易排查失败原因
- 使用 fixture 复用初始化逻辑:见
scripts/tests/conftest.py - Mock 外部服务:使用
unittest.mock或pytest-mock - 测试边界情况:空输入、None 值、错误情况
- 保持测试快速:避免
sleep()和外部 I/O - 使用 pytest 标记:例如
@pytest.mark.slow标记慢测试
Fixtures
常用 fixture 定义在 scripts/tests/conftest.py:
# 在测试中使用 fixture
def test_something(tmp_path):
"""tmp_path fixture 提供临时目录。"""
test_file = tmp_path / "test.txt"
test_file.write_text("content")
assert test_file.read_text() == "content"
覆盖率报告
本地覆盖率
# 生成覆盖率报告
pytest scripts/tests/ --cov=scripts --cov-report=html
# 在浏览器中打开覆盖率报告
open htmlcov/index.html
覆盖率目标
- 最低覆盖率:80%
- 分支覆盖率:启用
- 重点区域:核心功能和错误路径
Pre-commit Hooks
项目使用 pre-commit hooks 在每次提交前自动运行检查:
# 安装 pre-commit hooks
pre-commit install
# 手动运行 hooks
pre-commit run --all-files
# 跳过某次提交的 hooks(不推荐)
git commit --no-verify
在 .pre-commit-config.yaml 中配置的 hooks:
- Ruff formatter
- Ruff linter
- Bandit security scanner
- YAML validation
- 文件大小检查
- 合并冲突检测
排障
本地测试通过,但 CI 失败
常见原因:
- Python 版本差异:CI 使用 3.10、3.11、3.12
- 依赖缺失:更新
requirements-dev.txt - 平台差异:路径分隔符、环境变量
- 测试不稳定:依赖时序或执行顺序的测试
解决方案:
# 使用相同的 Python 版本测试
uv python install 3.10 3.11 3.12
# 使用干净环境测试
rm -rf .venv
uv venv
uv pip install -r requirements-dev.txt
pytest scripts/tests/
Bandit 报告误报
某些安全警告可能是误报。可在 pyproject.toml 中配置:
[tool.bandit]
exclude_dirs = ["scripts/tests"]
skips = ["B101"] # 跳过 assert_used 警告
类型检查太严格
对特定文件放宽类型检查:
# 放在文件顶部
# type: ignore
# 或者针对特定行
some_dynamic_code() # type: ignore
持续集成最佳实践
- 保持测试快速:每个测试最好在 1 秒内完成
- 不要测试外部 API:用 mock 替代外部服务
- 测试要隔离:每个测试都应独立
- 断言要清晰:写
assert x == 5,不要写assert x - 处理异步测试:使用
@pytest.mark.asyncio - 生成报告:覆盖率、安全扫描、类型检查
资源
- pytest Documentation
- Ruff Documentation
- Bandit Documentation
- mypy Documentation
- GitHub Actions Documentation
贡献测试
提交 PR 时:
- 为新功能编写测试
- 本地运行测试:
pytest scripts/tests/ -v - 检查覆盖率:
pytest scripts/tests/ --cov=scripts - 运行 lint:
ruff check scripts/ - 安全扫描:
bandit -r scripts/ --exclude scripts/tests/ - 如果测试变化,更新文档
所有 PR 都必须包含测试!🧪
如果你对测试有问题或疑问,请在 GitHub 上创建 issue 或 discussion。