# 测试规范 ## 一、测试概述 ### 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_.py` - 测试类:`Test` - 测试函数:`test_` **示例**: ```python # test_config.py class TestConfig: def test_load_success(self): """测试成功加载配置""" pass def test_load_file_not_found(self): """测试配置文件不存在""" pass ``` --- ## 三、单元测试 ### 3.1 core/output.py 测试 ```python # 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 测试 ```python # 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 测试 ```python # 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 测试 ```python # 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 测试 ```python # 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 测试 ```python # 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 路径处理测试 ```python 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 命令执行测试 ```python @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 执行时间测试 ```python 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 内存占用测试 ```python 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 运行覆盖率测试 ```bash # 运行测试并生成覆盖率报告 pytest --cov=src --cov-report=html --cov-report=term # 查看覆盖率报告 open htmlcov/index.html ``` ### 7.3 覆盖率配置 ```ini # .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 ```python # 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 配置 ```yaml # .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 测试原则 1. **独立性**:每个测试独立运行,不依赖其他测试 2. **可重复性**:多次运行结果一致 3. **清晰性**:测试意图明确,易于理解 4. **快速性**:测试执行快速,及时反馈 ### 10.2 测试模式 **AAA 模式**(Arrange-Act-Assert): ```python 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 使用 ```python 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 核心要点 1. 完整的测试覆盖(单元、集成、跨平台) 2. 明确的覆盖率要求 3. 持续集成自动化 4. 遵循测试最佳实践 ### 12.2 运行测试 ```bash # 运行所有测试 pytest # 运行特定测试 pytest tests/unit/test_config.py # 运行带覆盖率的测试 pytest --cov=src # 运行性能测试 pytest -m performance # 运行跨平台测试 pytest -m cross_platform ``` --- **版本**:v1.0 **更新日期**:2025-12-13