# 测试策略
**本文引用的文件**
- [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)