15 KiB
15 KiB
测试规范
一、测试概述
1.1 测试目标
确保 aide 程序的所有功能正确、稳定、可靠,满足设计要求。
1.2 测试范围
- 单元测试:核心模块和函数
- 集成测试:命令完整流程
- 跨平台测试:Linux、macOS、Windows
- 性能测试:执行时间和资源占用
1.3 测试工具
- pytest:测试框架
- pytest-cov:代码覆盖率
- pytest-mock:模拟和打桩
- pytest-timeout:超时控制
二、测试结构
2.1 目录结构
tests/
├── __init__.py
├── conftest.py # pytest 配置和 fixtures
├── unit/ # 单元测试
│ ├── __init__.py
│ ├── test_output.py # 输出模块测试
│ ├── test_config.py # 配置模块测试
│ └── test_validators.py # 验证函数测试
├── integration/ # 集成测试
│ ├── __init__.py
│ ├── test_init.py # aide init 测试
│ ├── test_env.py # aide env 测试
│ └── test_config_cmd.py # aide config 测试
└── fixtures/ # 测试数据
├── sample_config.toml
└── sample_gitignore
2.2 命名规范
- 测试文件:
test_<module>.py - 测试类:
Test<Feature> - 测试函数:
test_<scenario>
示例:
# test_config.py
class TestConfig:
def test_load_success(self):
"""测试成功加载配置"""
pass
def test_load_file_not_found(self):
"""测试配置文件不存在"""
pass
三、单元测试
3.1 core/output.py 测试
# tests/unit/test_output.py
import pytest
from core.output import ok, warn, err, info
def test_ok_simple(capsys):
"""测试简单成功输出"""
ok("操作成功")
captured = capsys.readouterr()
assert "✓ 操作成功" in captured.out
def test_ok_with_details(capsys):
"""测试带详细信息的成功输出"""
ok("操作成功", ["详细信息1", "详细信息2"])
captured = capsys.readouterr()
assert "✓ 操作成功" in captured.out
assert " 详细信息1" in captured.out
assert " 详细信息2" in captured.out
def test_warn(capsys):
"""测试警告输出"""
warn("这是警告")
captured = capsys.readouterr()
assert "⚠ 这是警告" in captured.out
def test_err(capsys):
"""测试错误输出"""
err("这是错误", ["建议: 检查配置"])
captured = capsys.readouterr()
assert "✗ 这是错误" in captured.out
assert " 建议: 检查配置" in captured.out
def test_info(capsys):
"""测试信息输出"""
info("正在处理...")
captured = capsys.readouterr()
assert "→ 正在处理..." in captured.out
3.2 core/config.py 测试
# tests/unit/test_config.py
import pytest
from pathlib import Path
from core.config import Config, get_config_value, set_config_value
def test_get_config_value():
"""测试获取配置值"""
config = {
"task": {
"source": "task-now.md"
}
}
assert get_config_value(config, "task.source") == "task-now.md"
assert get_config_value(config, "task.spec", "default") == "default"
def test_set_config_value():
"""测试设置配置值"""
config = {"task": {}}
set_config_value(config, "task.source", "new-task.md")
assert config["task"]["source"] == "new-task.md"
def test_config_load(tmp_path):
"""测试加载配置"""
# 创建测试配置文件
config_dir = tmp_path / ".aide"
config_dir.mkdir()
config_path = config_dir / "config.toml"
config_path.write_text('[task]\nsource = "test.md"\n')
# 加载配置
config = Config(tmp_path)
config.load()
assert config.get("task.source") == "test.md"
def test_config_load_not_found(tmp_path):
"""测试配置文件不存在"""
config = Config(tmp_path)
with pytest.raises(FileNotFoundError):
config.load()
def test_config_save(tmp_path):
"""测试保存配置"""
# 创建配置
config_dir = tmp_path / ".aide"
config_dir.mkdir()
config = Config(tmp_path)
config._config = {"task": {"source": "test.md"}}
config.save()
# 验证文件已创建
config_path = tmp_path / ".aide" / "config.toml"
assert config_path.exists()
# 验证内容
content = config_path.read_text()
assert "task" in content
assert "test.md" in content
3.3 utils/validators.py 测试
# tests/unit/test_validators.py
import pytest
from utils.validators import is_valid_version_spec, validate_config
def test_is_valid_version_spec():
"""测试版本规格验证"""
assert is_valid_version_spec(">=3.10") == True
assert is_valid_version_spec("3.12") == True
assert is_valid_version_spec(">=3.10,<4.0") == True
assert is_valid_version_spec("invalid") == False
def test_validate_config():
"""测试配置验证"""
# 有效配置
valid_config = {
"task": {"source": "task.md", "spec": "spec.md"},
"env": {"python": {"version": ">=3.10"}},
"output": {"language": "zh-CN"}
}
errors = validate_config(valid_config)
assert len(errors) == 0
# 缺少必需项
invalid_config = {"task": {}}
errors = validate_config(invalid_config)
assert len(errors) > 0
assert any("task.source" in e for e in errors)
四、集成测试
4.1 aide init 测试
# tests/integration/test_init.py
import pytest
from pathlib import Path
from commands.init import cmd_init
def test_init_success(tmp_path, monkeypatch):
"""测试初始化成功"""
monkeypatch.chdir(tmp_path)
result = cmd_init([])
assert result == 0
assert (tmp_path / ".aide").exists()
assert (tmp_path / ".aide" / "config.toml").exists()
assert (tmp_path / ".gitignore").exists()
def test_init_idempotent(tmp_path, monkeypatch):
"""测试幂等性"""
monkeypatch.chdir(tmp_path)
# 第一次初始化
result1 = cmd_init([])
assert result1 == 0
# 读取配置内容
config_path = tmp_path / ".aide" / "config.toml"
content1 = config_path.read_text()
# 第二次初始化
result2 = cmd_init([])
assert result2 == 0
# 验证配置未改变
content2 = config_path.read_text()
assert content1 == content2
def test_init_permission_error(tmp_path, monkeypatch):
"""测试权限错误"""
monkeypatch.chdir(tmp_path)
# 设置只读权限
tmp_path.chmod(0o444)
result = cmd_init([])
# 恢复权限
tmp_path.chmod(0o755)
assert result != 0
4.2 aide env 测试
# tests/integration/test_env.py
import pytest
from commands.env import cmd_env, env_ensure_runtime, env_ensure_project
def test_env_ensure_runtime_success():
"""测试运行时环境检测成功"""
result = env_ensure_runtime()
assert result == 0
def test_env_ensure_project_success(tmp_path, monkeypatch):
"""测试项目环境检测成功"""
monkeypatch.chdir(tmp_path)
# 初始化项目
from commands.init import cmd_init
cmd_init([])
# 检测环境
result = env_ensure_project()
assert result == 0
def test_env_ensure_project_no_config(tmp_path, monkeypatch):
"""测试配置文件不存在"""
monkeypatch.chdir(tmp_path)
result = env_ensure_project()
assert result == 4
def test_env_ensure_create_venv(tmp_path, monkeypatch):
"""测试自动创建虚拟环境"""
monkeypatch.chdir(tmp_path)
# 初始化项目
from commands.init import cmd_init
cmd_init([])
# 检测环境(应该自动创建虚拟环境)
result = env_ensure_project()
assert result == 0
# 验证虚拟环境已创建
assert (tmp_path / ".venv").exists()
4.3 aide config 测试
# tests/integration/test_config_cmd.py
import pytest
from commands.config_cmd import cmd_config, config_get, config_set
def test_config_get_success(tmp_path, monkeypatch, capsys):
"""测试获取配置成功"""
monkeypatch.chdir(tmp_path)
# 初始化
from commands.init import cmd_init
cmd_init([])
# 获取配置
result = config_get("task.source")
assert result == 0
captured = capsys.readouterr()
assert "task.source" in captured.out
assert "task-now.md" in captured.out
def test_config_set_success(tmp_path, monkeypatch):
"""测试设置配置成功"""
monkeypatch.chdir(tmp_path)
# 初始化
from commands.init import cmd_init
cmd_init([])
# 设置配置
result = config_set("task.source", "new-task.md")
assert result == 0
# 验证配置已更新
result = config_get("task.source")
assert result == 0
def test_config_cmd_no_args(capsys):
"""测试无参数"""
result = cmd_config([])
assert result == 2
captured = capsys.readouterr()
assert "缺少子命令" in captured.out
五、跨平台测试
5.1 路径处理测试
def test_path_handling_cross_platform(tmp_path):
"""测试跨平台路径处理"""
from pathlib import Path
# 使用 pathlib 确保跨平台兼容
aide_dir = tmp_path / ".aide"
config_path = aide_dir / "config.toml"
aide_dir.mkdir()
config_path.write_text("test")
assert config_path.exists()
assert config_path.is_file()
5.2 命令执行测试
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
def test_unix_specific():
"""Unix 特定测试"""
pass
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
def test_windows_specific():
"""Windows 特定测试"""
pass
六、性能测试
6.1 执行时间测试
import time
def test_init_performance(tmp_path, monkeypatch):
"""测试初始化性能"""
monkeypatch.chdir(tmp_path)
start = time.time()
result = cmd_init([])
elapsed = time.time() - start
assert result == 0
assert elapsed < 0.2 # 应该在 200ms 内完成
def test_config_get_performance(tmp_path, monkeypatch):
"""测试配置读取性能"""
monkeypatch.chdir(tmp_path)
# 初始化
cmd_init([])
# 测试性能
start = time.time()
result = config_get("task.source")
elapsed = time.time() - start
assert result == 0
assert elapsed < 0.1 # 应该在 100ms 内完成
6.2 内存占用测试
import psutil
import os
def test_memory_usage():
"""测试内存占用"""
process = psutil.Process(os.getpid())
mem_before = process.memory_info().rss / 1024 / 1024 # MB
# 执行操作
cmd_init([])
mem_after = process.memory_info().rss / 1024 / 1024 # MB
mem_used = mem_after - mem_before
assert mem_used < 50 # 应该小于 50MB
七、测试覆盖率
7.1 覆盖率要求
- 核心模块:≥ 80%
- 命令模块:≥ 80%
- 工具模块:≥ 70%
- 整体:≥ 75%
7.2 运行覆盖率测试
# 运行测试并生成覆盖率报告
pytest --cov=src --cov-report=html --cov-report=term
# 查看覆盖率报告
open htmlcov/index.html
7.3 覆盖率配置
# .coveragerc
[run]
source = src
omit =
*/tests/*
*/venv/*
*/__pycache__/*
[report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == .__main__.:
八、测试 Fixtures
8.1 conftest.py
# tests/conftest.py
import pytest
from pathlib import Path
import shutil
@pytest.fixture
def temp_project(tmp_path):
"""创建临时项目目录"""
project_dir = tmp_path / "test_project"
project_dir.mkdir()
return project_dir
@pytest.fixture
def initialized_project(temp_project, monkeypatch):
"""创建已初始化的项目"""
monkeypatch.chdir(temp_project)
from commands.init import cmd_init
cmd_init([])
return temp_project
@pytest.fixture
def sample_config():
"""示例配置"""
return {
"task": {
"source": "task-now.md",
"spec": "task-spec.md"
},
"env": {
"python": {
"version": ">=3.10",
"venv": ".venv"
}
}
}
九、持续集成
9.1 GitHub Actions 配置
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov pytest-mock
pip install tomli tomli-w
- name: Run tests
run: |
pytest --cov=src --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
十、测试最佳实践
10.1 测试原则
- 独立性:每个测试独立运行,不依赖其他测试
- 可重复性:多次运行结果一致
- 清晰性:测试意图明确,易于理解
- 快速性:测试执行快速,及时反馈
10.2 测试模式
AAA 模式(Arrange-Act-Assert):
def test_example():
# Arrange:准备测试数据
config = {"task": {"source": "test.md"}}
# Act:执行操作
result = get_config_value(config, "task.source")
# Assert:验证结果
assert result == "test.md"
10.3 Mock 使用
from unittest.mock import Mock, patch
def test_with_mock(monkeypatch):
"""使用 mock 测试"""
# Mock 函数
mock_func = Mock(return_value=True)
monkeypatch.setattr("module.function", mock_func)
# 执行测试
result = some_function()
# 验证 mock 被调用
mock_func.assert_called_once()
十一、测试检查清单
11.1 单元测试
- core/output.py 所有函数
- core/config.py 所有函数
- utils/validators.py 所有函数
- 边界条件测试
- 错误路径测试
11.2 集成测试
- aide init 完整流程
- aide env ensure --runtime
- aide env ensure
- aide config get
- aide config set
- 命令组合测试
11.3 跨平台测试
- Linux 测试通过
- macOS 测试通过
- Windows 测试通过
- 路径处理正确
11.4 性能测试
- 执行时间符合要求
- 内存占用符合要求
- 无内存泄漏
11.5 覆盖率
- 核心模块 ≥ 80%
- 命令模块 ≥ 80%
- 整体 ≥ 75%
十二、总结
12.1 核心要点
- 完整的测试覆盖(单元、集成、跨平台)
- 明确的覆盖率要求
- 持续集成自动化
- 遵循测试最佳实践
12.2 运行测试
# 运行所有测试
pytest
# 运行特定测试
pytest tests/unit/test_config.py
# 运行带覆盖率的测试
pytest --cov=src
# 运行性能测试
pytest -m performance
# 运行跨平台测试
pytest -m cross_platform
版本:v1.0 更新日期:2025-12-13