# 测试目录结构重构 **日期**: 2026-01-27 **类型**: 重构 **影响范围**: 测试代码组织 ## 背景 项目根目录下的 `test_sms_service.py` 和 `test_wechat_service.py` 是集成测试脚本,但位置和组织方式不符合标准测试规范: - ❌ 位于项目根目录,不易管理 - ❌ 未使用 pytest 框架 - ❌ 缺少测试分类(单元测试 vs 集成测试) - ❌ 缺少共享 fixtures 和配置 ## 解决方案 ### 1. 创建标准测试目录结构 ``` server/ ├── pytest.ini # pytest 配置文件 └── tests/ ├── __init__.py # 测试模块初始化 ├── conftest.py # pytest 配置和共享 fixtures ├── README.md # 测试文档 ├── integration/ # 集成测试 │ ├── __init__.py │ ├── test_sms_service.py │ └── test_wechat_service.py └── unit/ # 单元测试(预留) └── __init__.py ``` ### 2. 配置 pytest **pytest.ini**: ```ini [pytest] testpaths = tests python_files = test_*.py asyncio_mode = auto log_cli = true log_cli_level = INFO markers = integration: 集成测试(需要真实数据库、Redis) unit: 单元测试(使用 Mock) slow: 慢速测试 ``` ### 3. 创建共享 Fixtures **conftest.py**: ```python @pytest.fixture async def db_session(engine) -> AsyncGenerator[AsyncSession, None]: """创建数据库会话(自动回滚)""" async_session = sessionmaker( engine, class_=AsyncSession, expire_on_commit=False ) async with async_session() as session: yield session await session.rollback() @pytest.fixture async def redis_client() -> AsyncGenerator[Redis, None]: """创建 Redis 客户端(自动清理)""" client = Redis.from_url(settings.REDIS_URL) yield client await client.flushdb() await client.close() ``` ### 4. 重构测试文件 **改进点**: - ✅ 使用 pytest 装饰器(`@pytest.mark.asyncio`, `@pytest.mark.integration`) - ✅ 使用共享 fixtures(`db_session`, `redis_client`) - ✅ 拆分为独立的测试函数 - ✅ 添加断言和异常测试 - ✅ 保留直接运行支持(兼容旧方式) **示例**: ```python @pytest.mark.integration @pytest.mark.asyncio async def test_send_verification_code(db_session): """测试发送验证码""" sms_service = SmsService(db_session) result = await sms_service.send_verification_code( phone="15980906230", country_code="+86", purpose=SmsPurpose.LOGIN, ip_address="127.0.0.1" ) assert result is not None assert "message" in result assert result["expiresIn"] == 600 ``` ## 修改文件 ### 创建 - `server/pytest.ini` - pytest 配置 - `server/tests/__init__.py` - 测试模块初始化 - `server/tests/conftest.py` - 共享 fixtures - `server/tests/README.md` - 测试文档 - `server/tests/integration/__init__.py` - 集成测试模块 - `server/tests/integration/test_sms_service.py` - 短信服务测试(重构) - `server/tests/integration/test_wechat_service.py` - 微信服务测试(重构) - `server/tests/unit/__init__.py` - 单元测试模块(预留) ### 删除 - `server/test_sms_service.py` - 已移动到 `tests/integration/` - `server/test_wechat_service.py` - 已移动到 `tests/integration/` ## 运行测试 ### 使用 pytest(推荐) ```bash # 运行所有测试 pytest tests/ -v # 仅运行集成测试 pytest tests/integration/ -v # 运行特定测试文件 pytest tests/integration/test_sms_service.py -v # 运行特定测试函数 pytest tests/integration/test_sms_service.py::test_send_verification_code -v # 显示打印输出 pytest tests/ -v -s # 使用标记 pytest -m integration # 仅集成测试 pytest -m "not slow" # 跳过慢速测试 ``` ### 直接运行(兼容旧方式) ```bash # 仍然支持直接运行 python tests/integration/test_sms_service.py python tests/integration/test_wechat_service.py ``` ## 测试分类 ### 集成测试(Integration Tests) - **位置**: `tests/integration/` - **特点**: 使用真实数据库、Redis - **用途**: 验证完整业务流程 - **标记**: `@pytest.mark.integration` **现有测试**: - `test_sms_service.py` - 短信发送、验证、防刷、清理 - `test_wechat_service.py` - 二维码生成、登录、绑定、解绑 ### 单元测试(Unit Tests) - **位置**: `tests/unit/`(预留) - **特点**: 使用 Mock/Stub,不依赖外部服务 - **用途**: 测试单个函数或类的逻辑 - **标记**: `@pytest.mark.unit` **未来计划**: - Service 层业务逻辑测试 - Repository 层数据访问测试 - Utils 工具函数测试 ## 环境要求 ### 集成测试 需要以下服务运行: ```bash # 启动数据库和 Redis docker-compose up -d jointo-server-db jointo-server-redis # 运行数据库迁移 docker exec jointo-server-app alembic upgrade head # 运行测试 pytest tests/integration/ -v ``` ### 单元测试 不依赖外部服务,可以直接运行: ```bash pytest tests/unit/ -v ``` ## 优势 ### 1. 标准化 - ✅ 符合 pytest 最佳实践 - ✅ 清晰的目录结构 - ✅ 统一的测试规范 ### 2. 可维护性 - ✅ 共享 fixtures 减少重复代码 - ✅ 独立的测试函数便于调试 - ✅ 清晰的测试分类 ### 3. 可扩展性 - ✅ 预留单元测试目录 - ✅ 支持自定义标记 - ✅ 易于添加新测试 ### 4. CI/CD 友好 - ✅ 标准的 pytest 命令 - ✅ 支持覆盖率报告 - ✅ 支持并行测试 ## 后续计划 1. **添加单元测试** - Service 层业务逻辑测试 - Repository 层数据访问测试 - Utils 工具函数测试 2. **添加 API 测试** - 使用 TestClient 测试 FastAPI 路由 - 测试请求/响应格式 - 测试认证和权限 3. **添加性能测试** - 使用 pytest-benchmark - 测试关键接口性能 - 监控性能回归 4. **集成 CI/CD** - GitHub Actions 自动运行测试 - 生成覆盖率报告 - 测试失败时阻止合并 ## 相关文档 - [测试目录 README](../../server/tests/README.md) - [pytest 官方文档](https://docs.pytest.org/) - [pytest-asyncio 文档](https://pytest-asyncio.readthedocs.io/)