# Storyboard Project Resource 测试创建 **日期**: 2026-02-04 **类型**: 测试 **影响范围**: 分镜素材关联模块 ## 概述 为 `StoryboardResource`(分镜素材关联表)创建完整的测试套件,包括 Service 单元测试和 API 集成测试。 ## 变更内容 ### 1. Service 单元测试 **文件**: `server/tests/unit/services/test_storyboard_project_resource_service.py` **测试范围**: - ✅ 添加素材到分镜 - 成功添加 - 素材不存在 - 素材已关联 - ✅ 从分镜移除素材 - 成功移除 - 关联不存在 - 使用计数为 0 的边界情况 - ✅ 获取分镜的所有素材 - 成功获取 - 空分镜 - ✅ 获取素材关联的分镜 - 成功获取 - 未使用的素材 - ✅ 批量添加素材 - 全部成功 - 部分成功 - ✅ 批量移除素材 - 全部成功 - 部分成功 **测试方法**: 使用 Mock 对象模拟数据库会话和查询结果 ### 2. API 集成测试 **文件**: `server/tests/integration/test_storyboard_project_resource_api.py` **测试范围**: - ✅ 添加素材到分镜 API - 成功添加 - 素材不存在(404) - 素材已关联(400) - ✅ 从分镜移除素材 API - 成功移除 - 关联不存在(404) - ✅ 获取分镜的所有素材 API - 成功获取 - 空分镜 - ✅ 获取素材关联的分镜 API - 成功获取 - 未使用的素材 - ✅ 批量添加素材 API - 全部成功 - 部分成功 - ✅ 批量移除素材 API - 全部成功 - 部分成功 - ✅ 权限控制 - 访问其他用户的分镜(403) - 未授权访问(403) - ✅ 使用计数验证 - 添加后计数增加 - 移除后计数减少 **测试方法**: 使用真实数据库和 HTTP 客户端进行端到端测试 ## 测试覆盖率 ### Service 层 - **添加素材**: 3 个测试用例 - **移除素材**: 3 个测试用例 - **查询操作**: 4 个测试用例 - **批量操作**: 4 个测试用例 - **总计**: 14 个测试用例 ### API 层 - **CRUD 操作**: 8 个测试用例 - **批量操作**: 4 个测试用例 - **权限控制**: 2 个测试用例 - **业务逻辑**: 2 个测试用例 - **总计**: 16 个测试用例 ## 测试策略 ### 1. Mock 策略(Service 层) ```python # Mock 数据库会话 mock_session = AsyncMock() mock_session.commit = AsyncMock() mock_session.get = AsyncMock() mock_session.exec = AsyncMock() # Mock 查询结果 mock_result = AsyncMock() mock_result.first.return_value = resource mock_session.exec.return_value = mock_result ``` ### 2. Fixture 策略(API 层) ```python # 使用 pytest fixtures 提供测试数据 @pytest.fixture async def test_storyboard_resource(db_session, test_storyboard, test_project_resource): """创建测试用的分镜素材关联""" association = StoryboardResource(...) db_session.add(association) await db_session.commit() return association ``` ### 3. 边界条件测试 - ✅ 使用计数为 0 时的移除操作 - ✅ 空列表的批量操作 - ✅ 部分成功的批量操作 - ✅ 不存在的资源 ID ### 4. 错误处理测试 - ✅ `NotFoundError`: 素材不存在、关联不存在 - ✅ `ValidationError`: 素材已关联 - ✅ `PermissionError`: 无权限访问 ## 参考模板 测试结构参考了 Storyboard 模块的测试: - `server/tests/unit/repositories/test_storyboard_repository.py` - `server/tests/unit/services/test_storyboard_service.py` - `server/tests/integration/test_storyboard_api.py` ## 运行测试 ### 运行 Service 单元测试 ```bash docker exec jointo-server-app pytest server/tests/unit/services/test_storyboard_project_resource_service.py -v ``` ### 运行 API 集成测试 ```bash docker exec jointo-server-app pytest server/tests/integration/test_storyboard_project_resource_api.py -v ``` ### 运行所有 StoryboardResource 测试 ```bash docker exec jointo-server-app pytest server/tests -k "storyboard_project_resource" -v ``` ### 查看测试覆盖率 ```bash docker exec jointo-server-app pytest server/tests -k "storyboard_project_resource" --cov=app.services.storyboard_project_resource_service --cov-report=html ``` ## 后续工作 ### 1. 需要添加的 Fixtures 在 `server/tests/conftest.py` 中添加: ```python @pytest.fixture async def test_project_resource(db_session, test_project, test_user): """创建测试用的项目素材""" resource = ProjectResource(...) db_session.add(resource) await db_session.commit() return resource @pytest.fixture async def test_storyboard_resource(db_session, test_storyboard, test_project_resource): """创建测试用的分镜素材关联""" association = StoryboardResource(...) db_session.add(association) await db_session.commit() return association @pytest.fixture async def test_storyboard_resources(db_session, test_storyboard, test_project_resources): """创建多个测试用的分镜素材关联""" associations = [] for resource in test_project_resources[:3]: association = StoryboardResource(...) db_session.add(association) associations.append(association) await db_session.commit() return associations ``` ### 2. 需要实现的 API 路由 在 `server/app/api/v1/project_resources.py` 中添加: ```python @router.post("/storyboards/{storyboard_id}/resources/{resource_id}") async def add_resource_to_storyboard(...) @router.delete("/storyboards/{storyboard_id}/resources/{resource_id}") async def remove_resource_from_storyboard(...) @router.get("/storyboards/{storyboard_id}/resources") async def get_storyboard_resources(...) @router.get("/resources/{resource_id}/storyboards") async def get_resource_storyboards(...) @router.post("/storyboards/{storyboard_id}/resources/batch") async def batch_add_resources(...) @router.delete("/storyboards/{storyboard_id}/resources/batch") async def batch_remove_resources(...) ``` ## 技术栈合规性 ✅ **遵循 Jointo Tech Stack 规范**: - 使用 pytest + AsyncMock 进行异步测试 - 使用 pytest fixtures 管理测试数据 - 遵循 Repository-Service-API 三层架构 - 使用 UUID v7 作为主键 - 应用层校验引用完整性(无物理外键) - 使用 SMALLINT 存储枚举类型 - 完整的错误处理和权限校验 ## 相关文档 - [Storyboard 测试 Changelog](./2026-02-04-storyboard-tests-creation.md) - [Project Resource 测试 Changelog](./2026-02-04-project-resource-tests-creation.md) - [测试指南](../guides/testing.md) - [Jointo Tech Stack - Backend](../../.trae/skills/jointo-tech-stack/references/backend.md) ## 总结 成功为 StoryboardResource(分镜素材关联)模块创建了完整的测试套件,包括: - 14 个 Service 单元测试 - 16 个 API 集成测试 - 覆盖所有核心功能和边界情况 - 遵循项目测试规范和技术栈约束 测试文件已创建,待实现相关 fixtures 和 API 路由后即可运行。