# 测试策略 **本文引用的文件** - [tests/test_video_cli.py](file://tests/test_video_cli.py) - [tests/test_video_processor.py](file://tests/test_video_processor.py) - [tests/fixtures/README.md](file://tests/fixtures/README.md) - [requirements.txt](file://requirements.txt) - [PROJECT_README.md](file://PROJECT_README.md) - [main.py](file://main.py) - [backend/video_processor/__init__.py](file://backend/video_processor/__init__.py) - [backend/video_processor/schemas.py](file://backend/video_processor/schemas.py) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py) - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py) - [backend/video_processor/strategy_direct.py](file://backend/video_processor/strategy_direct.py) ## 目录 1. [引言](#引言) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 引言 本文件系统化阐述项目的测试策略与实践,重点覆盖: - tests/ 目录下的单元测试与集成测试设计原则 - 对 CLI 接口的独立测试工具 test_video_cli.py 的使用与验证路径 - 对视频处理模块 backend/video_processor 的单元测试与数据结构一致性校验 - 测试夹具(fixtures)的组织与生成方法 - 使用 pytest 运行测试套件的方法 - 在 CI/CD 中的测试职责与质量门禁建议 - 面向开发者的新增测试编写指南 目标是帮助开发者快速理解测试体系、可靠地扩展测试覆盖面,并确保后端逻辑的稳定性与正确性。 ## 项目结构 测试相关的关键位置与职责如下: - tests/test_video_cli.py:独立的 CLI 测试工具,用于直接调用视频分析模块,输出结果或进行依赖检查 - tests/test_video_processor.py:针对视频处理模块的单元测试,包含数据结构、工具函数与音频优先策略的端到端验证 - tests/fixtures/README.md:测试夹具(样本视频与期望输出)的生成与维护指南 - backend/video_processor/*:视频分析的核心实现,包括策略选择、数据结构定义与通用工具函数 - main.py:主入口脚本,展示视频分析在整体流水线中的位置(供理解测试上下文) ```mermaid graph TB subgraph "测试层" T1["tests/test_video_cli.py"] T2["tests/test_video_processor.py"] F["tests/fixtures/README.md"] end subgraph "后端视频处理" M1["backend/video_processor/__init__.py"] M2["backend/video_processor/schemas.py"] M3["backend/video_processor/utils.py"] M4["backend/video_processor/strategy_audio.py"] M5["backend/video_processor/strategy_direct.py"] end subgraph "主入口" E["main.py"] end T1 --> M1 T2 --> M1 T2 --> M2 T2 --> M3 T2 --> M4 T2 --> M5 E --> M1 ``` 图表来源 - [tests/test_video_cli.py](file://tests/test_video_cli.py#L1-L161) - [tests/test_video_processor.py](file://tests/test_video_processor.py#L1-L143) - [tests/fixtures/README.md](file://tests/fixtures/README.md#L1-L49) - [backend/video_processor/__init__.py](file://backend/video_processor/__init__.py#L1-L60) - [backend/video_processor/schemas.py](file://backend/video_processor/schemas.py#L1-L91) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L1-L164) - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py#L1-L187) - [backend/video_processor/strategy_direct.py](file://backend/video_processor/strategy_direct.py#L1-L58) - [main.py](file://main.py#L1-L305) 章节来源 - [PROJECT_README.md](file://PROJECT_README.md#L143-L156) ## 核心组件 - 视频分析入口与策略分发:backend/video_processor/__init__.py 提供 analyze_video 入口,根据 strategy 调用不同策略实现 - 数据结构契约:schemas.py 定义 VideoSegment、KeyMoment、VideoAnalysisResult 等标准输出结构,保证各策略输出一致 - 通用工具:utils.py 封装 FFmpeg/ffprobe 调用,提供时长查询、音频提取、视频切片与依赖检测 - 策略实现: - strategy_audio.py 实现“音频优先”策略,调用 OpenAI Whisper 获取带时间戳的转录,再生成片段与关键时刻 - strategy_direct.py 为“直接视频分析”占位,当前抛出未实现异常,留待后续迭代 章节来源 - [backend/video_processor/__init__.py](file://backend/video_processor/__init__.py#L1-L60) - [backend/video_processor/schemas.py](file://backend/video_processor/schemas.py#L1-L91) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L1-L164) - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py#L1-L187) - [backend/video_processor/strategy_direct.py](file://backend/video_processor/strategy_direct.py#L1-L58) ## 架构总览 测试与被测模块的交互关系如下: ```mermaid sequenceDiagram participant CLI as "CLI 测试工具
tests/test_video_cli.py" participant API as "分析入口
backend/video_processor/__init__.py" participant Strat as "音频优先策略
strategy_audio.py" participant Utils as "工具函数
utils.py" participant Schemas as "数据结构
schemas.py" CLI->>API : 调用 analyze_video(video_path, strategy, config) API->>Strat : 选择策略并实例化 Strat->>Utils : 提取音频/获取时长 Strat->>Strat : Whisper 转录带时间戳 Strat->>Schemas : 构造 VideoSegment/KeyMoment/VideoAnalysisResult Strat-->>API : 返回 VideoAnalysisResult API-->>CLI : 返回结果并可转换为字典 ``` 图表来源 - [tests/test_video_cli.py](file://tests/test_video_cli.py#L100-L145) - [backend/video_processor/__init__.py](file://backend/video_processor/__init__.py#L31-L60) - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py#L54-L100) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L13-L164) - [backend/video_processor/schemas.py](file://backend/video_processor/schemas.py#L12-L91) ## 详细组件分析 ### CLI 测试工具:test_video_cli.py - 设计目的:独立验证视频分析模块,支持依赖检查、策略选择、语言设置、对比模式与输出到 JSON 文件 - 关键行为: - 依赖检查:验证 FFmpeg 与 OpenAI 库是否可用 - 输入校验:要求提供输入视频文件;若仅需检查依赖,可通过参数触发 - 分析执行:调用 analyze_video 并将结果转换为字典输出 - 输出控制:默认打印到控制台,也可写入 JSON 文件 - 适用场景: - 快速验证单个视频的分析流程 - 作为回归测试的辅助工具,配合 fixtures 生成期望输出 章节来源 - [tests/test_video_cli.py](file://tests/test_video_cli.py#L1-L161) ### 单元测试:test_video_processor.py - 测试范围: - 数据结构:VideoSegment、KeyMoment、VideoAnalysisResult 的创建与序列化 - 工具函数:FFmpeg 依赖检测、视频时长获取、音频提取(含清理) - 策略验证:音频优先策略在具备 OPENAI_API_KEY 和 sample.mp4 时的端到端验证 - 测试夹具: - sample.mp4:短时长日语对话视频,用于测试处理链路 - expected_output.json:由 CLI 工具生成的期望输出,用于回归比对 - 标记与跳过: - 使用 pytest.mark.skipif 条件跳过:当环境变量或文件缺失时跳过对应测试,避免非必要的失败 - 断言要点: - 结果对象的字段完整性与类型正确性 - 分析结果中片段数量、重要片段过滤、引号类关键时刻等业务断言 章节来源 - [tests/test_video_processor.py](file://tests/test_video_processor.py#L1-L143) - [tests/fixtures/README.md](file://tests/fixtures/README.md#L1-L49) ### 数据结构与契约:schemas.py - VideoSegment:包含起止时间、描述、转录、重要度、标签、说话人等字段,并提供 to_dict 序列化 - KeyMoment:包含时间戳、类型(quote/chart/emotion/action)、内容与上下文,并提供 to_dict 序列化 - VideoAnalysisResult:包含源视频、总时长、策略、片段列表、关键时刻列表、元数据,并提供过滤方法与 to_dict 序列化 - 契约意义:统一各策略输出格式,便于上层流水线消费与渲染 章节来源 - [backend/video_processor/schemas.py](file://backend/video_processor/schemas.py#L1-L91) ### 工具函数:utils.py - get_video_duration:基于 ffprobe 获取视频时长,错误时抛出运行时异常 - extract_audio:基于 ffmpeg 提取音频,支持采样率与编码器配置,返回音频文件路径 - slice_video:基于 ffmpeg 进行流复制切片,不重编码,适合快速片段提取 - check_ffmpeg_installed:检查 ffmpeg/ffprobe 是否在 PATH 中 章节来源 - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L1-L164) ### 策略实现:strategy_audio.py - 初始化:从配置读取语言、模型与提示词,并从环境变量读取 OPENAI_API_KEY - analyze:按以下步骤执行 - 提取音频 - Whisper 转录(verbose_json,带段级时间戳) - 将转录段落转换为 VideoSegment 列表 - 识别关键时刻(示例:标记首段为 quote) - 获取视频总时长 - 清理临时音频文件 - 组装 VideoAnalysisResult 返回 - 错误处理:缺少 OPENAI_API_KEY 时抛出异常;FFmpeg/Whisper 失败时抛出运行时异常 章节来源 - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py#L1-L187) ### 策略实现:strategy_direct.py(占位) - 当前状态:抛出 NotImplementedError,提示使用音频优先策略 - 后续演进:按计划拆分视频、上传至 Gemini、分析视觉与音频、合并结果 章节来源 - [backend/video_processor/strategy_direct.py](file://backend/video_processor/strategy_direct.py#L1-L58) ### 测试夹具(fixtures)管理 - 目录与用途: - tests/fixtures/sample.mp4:测试用短视频(git-ignored),用于验证处理链路 - tests/fixtures/expected_output.json:由 CLI 工具生成的期望输出,用于回归测试 - 生成流程: - 准备 sample.mp4(录制或下载) - 使用 CLI 工具生成 expected_output.json - 在测试中加载该文件进行比对(如需) 章节来源 - [tests/fixtures/README.md](file://tests/fixtures/README.md#L1-L49) ### 测试运行与环境 - 运行方式: - 使用 pytest 运行 tests/test_video_processor.py - 或在根目录使用命令行运行 pytest - 依赖安装: - requirements.txt 中包含 pytest 与 pytest-cov 的注释说明,可在需要时启用覆盖率统计 - 环境变量: - OPENAI_API_KEY:音频优先策略依赖 Whisper - FFmpeg/ffprobe:工具函数依赖 章节来源 - [PROJECT_README.md](file://PROJECT_README.md#L151-L156) - [requirements.txt](file://requirements.txt#L31-L34) ## 依赖关系分析 - 模块耦合: - analyze_video 作为入口,仅依赖策略实现与工具函数,保持良好解耦 - 策略实现依赖工具函数与数据结构,不反向依赖上层 - 外部依赖: - FFmpeg/ffprobe:媒体处理 - OpenAI Whisper:音频转录 - 可能的循环依赖: - 当前结构清晰,无明显循环导入 ```mermaid graph LR Init["__init__.py
analyze_video"] --> SA["strategy_audio.py"] Init --> SD["strategy_direct.py"] SA --> U["utils.py"] SA --> S["schemas.py"] SD --> S ``` 图表来源 - [backend/video_processor/__init__.py](file://backend/video_processor/__init__.py#L31-L60) - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py#L54-L100) - [backend/video_processor/strategy_direct.py](file://backend/video_processor/strategy_direct.py#L43-L58) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L13-L164) - [backend/video_processor/schemas.py](file://backend/video_processor/schemas.py#L12-L91) ## 性能考量 - 音频优先策略的优势: - 以文本替代视频帧,显著降低 Token 成本与处理时间 - Whisper 提供精确的时间戳,有利于后续片段定位 - 工具函数的性能注意: - extract_audio 与 slice_video 使用流复制,避免重编码,提升速度 - get_video_duration 依赖 ffprobe,应确保其可用性与版本兼容 - 测试中的性能关注点: - 使用 skipif 跳过资源不足的测试,减少无效等待 - 在本地使用小体积 sample.mp4 进行快速回归 章节来源 - [backend/video_processor/strategy_audio.py](file://backend/video_processor/strategy_audio.py#L1-L187) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L13-L164) - [tests/test_video_processor.py](file://tests/test_video_processor.py#L80-L140) ## 故障排查指南 - 依赖缺失 - FFmpeg/ffprobe 未安装:使用 CLI 的依赖检查功能确认安装 - OPENAI_API_KEY 未设置:音频优先策略会抛出异常,需在环境变量中配置 - 输入问题 - 缺少 sample.mp4:按 fixtures 说明准备测试视频 - 视频路径不存在:CLI 会报错并退出 - 工具函数异常 - get_video_duration/extract_audio/slice_video:当外部命令失败时抛出运行时异常,检查命令行输出与权限 - 测试失败 - 跳过测试:检查 skipif 条件(环境变量、文件存在性) - 回归比对:核对 expected_output.json 与实际输出差异 章节来源 - [tests/test_video_cli.py](file://tests/test_video_cli.py#L70-L102) - [tests/test_video_processor.py](file://tests/test_video_processor.py#L80-L140) - [backend/video_processor/utils.py](file://backend/video_processor/utils.py#L13-L164) - [tests/fixtures/README.md](file://tests/fixtures/README.md#L1-L49) ## 结论 本项目的测试策略围绕“数据结构契约 + 工具函数封装 + 策略实现”的清晰边界展开,既保证了模块内聚,又便于独立验证与扩展。通过 CLI 测试工具与 pytest 单元测试相结合,能够快速定位问题、稳定回归,并为后续引入更多策略与集成测试打下基础。建议在 CI/CD 中加入: - 必要依赖检查(FFmpeg/ffprobe) - 环境变量校验(OPENAI_API_KEY) - 覆盖率统计(可选) - 回归测试(基于 fixtures 与 expected_output.json) ## 附录 ### 新增测试编写指南 - 遵循的原则 - 以数据结构为中心:先验证 schemas 的创建与序列化 - 以工具函数为边界:验证外部命令可用性与错误分支 - 以策略为场景:在具备必要凭据与资源的前提下验证端到端流程 - 夹具准备 - 在 tests/fixtures 下添加新的样本文件,并在测试中使用 skipif 保护 - 如需生成期望输出,使用 CLI 工具导出 JSON - 运行与调试 - 使用 pytest 运行指定测试文件或模块 - 通过 -v/-k 等选项筛选与调试 - CI/CD 集成建议 - 在流水线中先行安装依赖与环境变量 - 执行 pytest 并收集覆盖率报告(可选) - 对关键测试(如音频优先策略)进行条件执行 章节来源 - [tests/test_video_processor.py](file://tests/test_video_processor.py#L1-L143) - [tests/test_video_cli.py](file://tests/test_video_cli.py#L1-L161) - [tests/fixtures/README.md](file://tests/fixtures/README.md#L1-L49) - [PROJECT_README.md](file://PROJECT_README.md#L151-L156)