# 测试框架

<cite>
**本文引用的文件**
- [jest.config.js](file://jest.config.js)
- [jest.config.js](file://client/jest.config.js)
- [jest.setup.js](file://client/jest.setup.js)
- [package.json](file://package.json)
- [package.json](file://client/package.json)
- [httphandler.test.ts](file://test/httphandler.test.ts)
- [websockethandler.test.ts](file://test/websockethandler.test.ts)
- [signaling.test.js](file://client/test/signaling.test.js)
- [mocksignaling.js](file://client/test/mocksignaling.js)
- [peerconnection.test.js](file://client/test/peerconnection.test.js)
- [peerconnectionmock.js](file://client/test/peerconnectionmock.js)
- [resizeobservermock.js](file://client/test/resizeobservermock.js)
- [testutils.js](file://client/test/testutils.js)
- [sender.test.js](file://client/test/sender.test.js)
- [httphandler.ts](file://src/class/httphandler.ts)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本指南面向视频流服务器项目的测试体系，系统性阐述测试策略与架构，覆盖单元测试、集成测试与端到端测试的组织方式；详解 Jest 测试框架在服务端与客户端的配置与使用，包括测试环境设置、模拟对象与工具库；提供 WebRTC 信令与数据通道输入远程控制等关键功能的测试示例；说明测试覆盖率要求与报告生成；总结测试最佳实践与调试技巧，并给出测试用例编写指南。

## 项目结构
项目采用分层与按功能划分的组织方式：
- 服务端（src）：核心业务逻辑与 HTTP/WebSocket 信令处理
- 服务端测试（test）：基于 Express 模拟与 WebSocket 模拟器的集成测试
- 客户端（client）：前端 JS 模块与 WebRTC 交互逻辑
- 客户端测试（client/test）：JSDOM 环境下的单元与集成测试，含 WebRTC 模拟与信令模拟

```mermaid
graph TB
subgraph "服务端"
S1["HTTP处理器<br/>src/class/httphandler.ts"]
S2["WebSocket处理器<br/>src/class/websockethandler.ts"]
end
subgraph "服务端测试"
T1["HTTP信令测试<br/>test/httphandler.test.ts"]
T2["WebSocket信令测试<br/>test/websockethandler.test.ts"]
end
subgraph "客户端"
C1["信令模块<br/>client/src/signaling.js"]
C2["WebRTC封装<br/>client/src/peer.js"]
C3["输入远程控制<br/>client/src/inputremoting.js"]
end
subgraph "客户端测试"
CT1["信令测试<br/>client/test/signaling.test.js"]
CT2["Peer连接测试<br/>client/test/peerconnection.test.js"]
CT3["发送器测试<br/>client/test/sender.test.js"]
CM1["信令模拟<br/>client/test/mocksignaling.js"]
CM2["Peer模拟<br/>client/test/peerconnectionmock.js"]
CU1["测试工具<br/>client/test/testutils.js"]
end
T1 --> S1
T2 --> S2
CT1 --> C1
CT2 --> C2
CT3 --> C3
CT1 --> CM1
CT2 --> CM2
CT2 --> CU1
```

图表来源
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)
- [peerconnection.test.js:1-251](file://client/test/peerconnection.test.js#L1-L251)
- [mocksignaling.js:1-225](file://client/test/mocksignaling.js#L1-L225)
- [peerconnectionmock.js:1-316](file://client/test/peerconnectionmock.js#L1-L316)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)

章节来源
- [jest.config.js:1-196](file://jest.config.js#L1-L196)
- [jest.config.js:1-196](file://client/jest.config.js#L1-L196)
- [package.json:1-60](file://package.json#L1-L60)
- [package.json:1-19](file://client/package.json#L1-L19)

## 核心组件
- 测试运行器与配置
  - 服务端 Jest 配置启用 TypeScript 转换、Node 环境、覆盖率收集与默认匹配规则
  - 客户端 Jest 配置启用 JSDOM 环境、setupFilesAfterEnv 引入全局模拟与 WebRTC 模拟
- 测试工具与模拟
  - 服务端：Express 模拟中间件与 WebSocket 模拟器，验证 HTTP/WebSocket 信令流程
  - 客户端：JSDOM 环境下注入 fetch、TextEncoder/Decoder、RTCPeerConnection 及 ResizeObserver 的模拟实现
  - 信令模拟：MockSignaling 与 MockPublic/PrivateSignalingManager，支持公共/私有模式下的信令广播与连接管理
  - WebRTC 模拟：PeerConnectionMock、SessionDescriptionMock、IceCandidateMock，覆盖 SDP 描述、ICE 候选、事件触发与状态机
  - 工具库：waitFor、sleep、getRTCConfiguration 等，支撑异步等待与测试配置
- 关键模块测试
  - HTTP 信令：会话创建、连接创建、offer/answer/candidate 收发、断开清理与超时回收
  - WebSocket 信令：连接建立、消息转发、断开通知与多客户端广播
  - 信令集成：公共/私有模式下跨客户端的信令收发与连接极性判定
  - WebRTC Peer：addTrack/addTransceiver/createDataChannel、协商事件、候选收集、错误状态抛出
  - 输入远程控制：鼠标/键盘/触摸事件捕获与 RTC 数据通道发送

章节来源
- [jest.config.js:1-196](file://jest.config.js#L1-L196)
- [jest.config.js:1-196](file://client/jest.config.js#L1-L196)
- [jest.setup.js:1-35](file://client/jest.setup.js#L1-L35)
- [mocksignaling.js:1-225](file://client/test/mocksignaling.js#L1-L225)
- [peerconnectionmock.js:1-316](file://client/test/peerconnectionmock.js#L1-L316)
- [resizeobservermock.js:1-18](file://client/test/resizeobservermock.js#L1-L18)
- [testutils.js:1-39](file://client/test/testutils.js#L1-L39)
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)
- [peerconnection.test.js:1-251](file://client/test/peerconnection.test.js#L1-L251)
- [sender.test.js:1-143](file://client/test/sender.test.js#L1-L143)

## 架构总览
测试架构分为三层：
- 单元测试层：针对纯函数与小模块（如工具函数、模型类），快速验证边界条件与异常分支
- 集成测试层：组合模块与外部依赖（Express 中间件、WebSocket 服务器），验证端到端流程
- 端到端测试层：启动真实服务进程，进行跨浏览器/设备的信令与媒体交互验证

```mermaid
sequenceDiagram
participant Test as "测试用例"
participant HTTP as "HTTP处理器<br/>httphandler.ts"
participant WS as "WebSocket处理器<br/>websockethandler.ts"
participant MockSig as "信令模拟<br/>mocksignaling.js"
participant Peer as "Peer连接模拟<br/>peerconnectionmock.js"
Test->>HTTP : "创建会话/连接"
HTTP-->>Test : "返回JSON响应"
Test->>MockSig : "创建连接/发送offer/answer/candidate"
MockSig-->>Test : "触发connect/offer/answer/candidate事件"
Test->>Peer : "onGotDescription/onGotCandidate"
Peer-->>Test : "触发sendoffer/sendanswer/sendcandidate事件"
Test->>WS : "WebSocket信令收发"
WS-->>Test : "消息转发/断开通知"
```

图表来源
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)
- [mocksignaling.js:1-225](file://client/test/mocksignaling.js#L1-L225)
- [peerconnectionmock.js:1-316](file://client/test/peerconnectionmock.js#L1-L316)

## 详细组件分析

### 服务端 HTTP 信令测试（单元/集成）
- 测试目标
  - 会话生命周期：创建、检查、删除
  - 连接生命周期：创建、断开、清理
  - 信令消息：offer/answer/candidate 的收发与查询
  - 模式差异：公共/私有模式下的连接极性与广播行为
  - 超时机制：长时间无请求的会话清理
- 关键断言
  - 响应状态码与 JSON 结构
  - 会话内连接列表与消息队列
  - 私有模式下仅配对连接可互相接收信令
- 异步与超时
  - 使用定时器与延迟模拟，验证超时清理逻辑

```mermaid
flowchart TD
Start(["开始"]) --> CreateSession["创建会话"]
CreateSession --> CreateConn["创建连接"]
CreateConn --> SendOffer["发送Offer"]
SendOffer --> ReceiveOffer["接收Offer"]
ReceiveOffer --> SendAnswer["发送Answer"]
SendAnswer --> ReceiveAnswer["接收Answer"]
ReceiveAnswer --> SendCandidate["发送Candidate"]
SendCandidate --> ReceiveCandidate["接收Candidate"]
ReceiveCandidate --> DeleteConn["删除连接"]
DeleteConn --> Cleanup["清理会话/断开通知"]
Cleanup --> End(["结束"])
```

图表来源
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)

章节来源
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)

### 服务端 WebSocket 信令测试（集成）
- 测试目标
  - 多客户端连接与消息广播
  - 私有模式下仅配对连接可互相接收信令
  - 断开连接时双向通知
- 关键断言
  - WebSocket 消息协议字段（from/to/type/data）
  - 消息到达顺序与数量

```mermaid
sequenceDiagram
participant C1 as "客户端1"
participant C2 as "客户端2"
participant WS as "WebSocket服务器"
C1->>WS : "connect(connectionId)"
WS-->>C1 : "connect(own)"
WS-->>C2 : "connect(other)"
C1->>WS : "offer({connectionId,sdp})"
WS-->>C2 : "offer({from,to,type,data})"
C2->>WS : "answer({connectionId,sdp})"
WS-->>C1 : "answer({from,to,type,data})"
C1->>WS : "disconnect(connectionId)"
WS-->>C1 : "disconnect"
WS-->>C2 : "disconnect"
```

图表来源
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)

章节来源
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)

### 客户端信令测试（集成/端到端）
- 测试目标
  - 公共/私有模式下信令收发与连接极性
  - HTTP 与 WebSocket 两种信令通道
  - 本地 MockSignaling 与真实服务进程的对比
- 关键断言
  - connect/offer/answer/candidate 事件的 detail 字段
  - 断开事件触发次数与 connectionId 匹配
- 端到端验证
  - 通过 jest-dev-server 启动服务进程，使用真实 Signaling/WebSocketSignaling 进行连通性测试

```mermaid
sequenceDiagram
participant T as "测试用例"
participant M as "MockSignaling"
participant S as "Signaling(HTTP)"
participant W as "WebSocketSignaling"
participant P as "服务进程"
T->>M : "start/createConnection/sendOffer"
M-->>T : "dispatchEvent(connect/offer/answer/candidate)"
T->>S : "start/createConnection/sendOffer"
S->>P : "HTTP请求"
P-->>S : "响应/消息存储"
T->>W : "start/createConnection/sendOffer"
W->>P : "WebSocket消息"
P-->>W : "消息转发"
```

图表来源
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)
- [mocksignaling.js:1-225](file://client/test/mocksignaling.js#L1-L225)

章节来源
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)
- [mocksignaling.js:1-225](file://client/test/mocksignaling.js#L1-L225)

### WebRTC Peer 连接测试（单元/集成）
- 测试目标
  - 构造函数初始化与事件监听器绑定
  - addTrack/addTransceiver/createDataChannel 触发协商
  - onGotDescription/onGotCandidate 的事件与状态变化
  - 候选收集与错误状态抛出
- 关键断言
  - 发送 offer/answer/candidate 的事件详情
  - negotiated 事件触发时机
  - 候选未被接受的边界条件

```mermaid
classDiagram
class Peer {
+connectionId
+pc
+addEventListener(event, handler)
+close()
+addTrack(connectionId, track)
+addTransceiver(connectionId, kind)
+createDataChannel(connectionId, label)
+onGotDescription(connectionId, desc)
+onGotCandidate(connectionId, candidate)
}
class PeerConnectionMock {
+localDescription
+remoteDescription
+addTrack(track)
+addTransceiver(kind)
+createDataChannel(label)
+setLocalDescription(desc)
+setRemoteDescription(desc)
+addIceCandidate(candidate)
+close()
}
Peer --> PeerConnectionMock : "使用"
```

图表来源
- [peerconnection.test.js:1-251](file://client/test/peerconnection.test.js#L1-L251)
- [peerconnectionmock.js:1-316](file://client/test/peerconnectionmock.js#L1-L316)

章节来源
- [peerconnection.test.js:1-251](file://client/test/peerconnection.test.js#L1-L251)
- [peerconnectionmock.js:1-316](file://client/test/peerconnectionmock.js#L1-L316)

### 输入远程控制测试（单元）
- 测试目标
  - 设备注册与事件订阅
  - 鼠标/键盘/触摸事件序列化与发送
  - RTC 数据通道状态与发送调用
- 关键断言
  - 设备数量与事件回调注册
  - 数据通道 send 调用次数与 ArrayBuffer 内容

章节来源
- [sender.test.js:1-143](file://client/test/sender.test.js#L1-L143)

## 依赖分析
- 测试运行与环境
  - 服务端：Jest + ts-jest + @jest-mock/express + jest-websocket-mock
  - 客户端：Jest + jest-environment-jsdom + jest-dev-server + node-fetch
- 模拟与工具
  - WebRTC 模拟：RTCPeerConnection、SessionDescription、IceCandidate
  - DOM/ResizeObserver 模拟：JSDOM 环境缺失时的 polyfill
  - 信令模拟：MockSignaling 与管理器
  - 工具函数：等待、睡眠、唯一 ID、RTC 配置

```mermaid
graph LR
Jest["Jest"] --> TS["ts-jest"]
Jest --> JSDOM["jest-environment-jsdom"]
Jest --> DevServer["jest-dev-server"]
Jest --> WS["jest-websocket-mock"]
Client["客户端测试"] --> JSDOM
Client --> DevServer
Client --> Fetch["node-fetch"]
Server["服务端测试"] --> ExpressMock["@jest-mock/express"]
Server --> WS
```

图表来源
- [jest.config.js:1-196](file://jest.config.js#L1-L196)
- [jest.config.js:1-196](file://client/jest.config.js#L1-L196)
- [package.json:1-60](file://package.json#L1-L60)
- [package.json:1-19](file://client/package.json#L1-L19)

章节来源
- [jest.config.js:1-196](file://jest.config.js#L1-L196)
- [jest.config.js:1-196](file://client/jest.config.js#L1-L196)
- [package.json:1-60](file://package.json#L1-L60)
- [package.json:1-19](file://client/package.json#L1-L19)

## 性能考虑
- 测试并发与超时
  - 客户端测试配置了较短的 testTimeout，避免长耗时阻塞
  - 使用延迟与等待函数替代硬编码 sleep，提升稳定性
- 覆盖率与报告
  - 启用 v8 覆盖收集与输出目录，建议结合 CI 生成 LCOV 报告
  - 对关键路径（HTTP/WebSocket 信令、WebRTC 状态机）优先保证高覆盖率
- 端到端成本
  - 真实服务进程启动与关闭需注意资源释放，Linux 平台增加等待时间

章节来源
- [jest.config.js:170-171](file://client/jest.config.js#L170-L171)
- [jest.config.js:20-26](file://jest.config.js#L20-L26)
- [jest.config.js:33-34](file://jest.config.js#L33-L34)

## 故障排查指南
- 环境变量与脚本
  - 服务端脚本使用 Jest 直接执行 test/*.ts；确保 ts-jest 与 TypeScript 配置正确
  - 客户端脚本使用 Node VM 模块运行 Jest；确保 package.json 中的脚本与依赖一致
- JSDOM 缺失 API
  - 在 jest.setup.js 中注入 fetch、TextEncoder/Decoder、RTCPeerConnection、ResizeObserver
  - 若仍报错，确认 jest-environment-jsdom 版本与 jest.setup.js 的引入顺序
- WebRTC 模拟问题
  - 确认 PeerConnectionMock 的事件回调与状态机逻辑与实际浏览器行为一致
  - 对于 InvalidStateError 等异常，需在测试中显式断言
- 信令模拟一致性
  - MockSignaling 与真实服务端的字段保持一致（connectionId、sdp、polite、candidate 等）
  - 私有模式下仅配对连接可互相接收信令，否则应断言未收到
- 端到端启动失败
  - 检查 jest-dev-server 的命令拼接与端口占用
  - Linux 平台在 teardown 后增加等待时间，避免进程残留导致后续测试失败

章节来源
- [package.json:5-12](file://package.json#L5-L12)
- [package.json:5-8](file://client/package.json#L5-L8)
- [jest.setup.js:1-35](file://client/jest.setup.js#L1-L35)
- [signaling.test.js:23-65](file://client/test/signaling.test.js#L23-L65)
- [peerconnectionmock.js:157-160](file://client/test/peerconnectionmock.js#L157-L160)

## 结论
本项目采用“单元测试 + 集成测试 + 端到端测试”的分层策略，结合 Jest 与多种模拟技术，实现了对 HTTP/WebSocket 信令、WebRTC 连接与输入远程控制的全面覆盖。通过 MockSignaling、PeerConnectionMock 与工具库，测试在可控环境中复现复杂交互；通过公共/私有模式与超时清理逻辑的验证，确保系统在真实场景下的健壮性。

## 附录

### 测试策略与组织
- 单元测试
  - 针对纯函数与小模块，断言边界与异常
  - 示例：工具函数、模型类、状态转换
- 集成测试
  - 组合模块与外部依赖，验证流程完整性
  - 示例：HTTP 信令、WebSocket 信令、WebRTC 状态机
- 端到端测试
  - 启动真实服务进程，验证跨客户端交互
  - 示例：HTTP/WebSocket 信令通道、真实浏览器环境

章节来源
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)

### Jest 配置与使用要点
- 服务端
  - 启用 ts-jest 转换，Node 环境，收集覆盖率，匹配 test/*.ts
- 客户端
  - JSDOM 环境，setupFilesAfterEnv 引入全局模拟，匹配 client/test/**/*.test.js
- 脚本
  - 服务端：npm/yarn test 指向 Jest 并传入测试文件
  - 客户端：使用 Node VM 模块运行 Jest

章节来源
- [jest.config.js:1-196](file://jest.config.js#L1-L196)
- [jest.config.js:1-196](file://client/jest.config.js#L1-L196)
- [package.json:5-12](file://package.json#L5-L12)
- [package.json:5-8](file://client/package.json#L5-L8)

### 覆盖率要求与报告
- 覆盖率收集
  - 开启 collectCoverage，输出目录为 coverage，提供 v8 覆盖器
- 报告生成
  - 建议在 CI 中生成 LCOV 报告并与平台集成
- 要求建议
  - 关键模块（HTTP/WebSocket 信令、WebRTC 状态机）覆盖率不低于 80%
  - 重要分支与异常路径必须覆盖

章节来源
- [jest.config.js:20-26](file://jest.config.js#L20-L26)
- [jest.config.js:33-34](file://jest.config.js#L33-L34)

### WebRTC 功能测试示例
- 信令处理测试
  - 使用 MockSignaling 验证公共/私有模式下的 offer/answer/candidate 分发
  - 使用 jest-websocket-mock 验证 WebSocket 信令通道
- Peer 连接测试
  - 使用 PeerConnectionMock 验证 addTrack/addTransceiver/createDataChannel 的事件触发
  - 验证 onGotDescription/onGotCandidate 的状态变化与候选收集

章节来源
- [mocksignaling.js:1-225](file://client/test/mocksignaling.js#L1-L225)
- [peerconnectionmock.js:1-316](file://client/test/peerconnectionmock.js#L1-L316)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)

### 客户端测试与服务器端测试示例
- 客户端
  - 信令：公共/私有模式、HTTP/WebSocket 三种通道
  - WebRTC：构造、关闭、事件触发、候选收集
  - 输入远程控制：事件捕获与发送
- 服务器端
  - HTTP 信令：会话/连接生命周期、消息收发、超时清理
  - WebSocket 信令：多客户端广播、断开通知

章节来源
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)
- [peerconnection.test.js:1-251](file://client/test/peerconnection.test.js#L1-L251)
- [sender.test.js:1-143](file://client/test/sender.test.js#L1-L143)
- [httphandler.test.ts:1-510](file://test/httphandler.test.ts#L1-L510)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)

### 测试最佳实践与调试技巧
- 最佳实践
  - 使用 describe/it 分层组织测试，命名清晰表达意图
  - 使用 beforeEach/beforeAll 准备测试上下文，避免重复代码
  - 对异步流程使用 waitFor/sleep 替代固定延时
  - 对模拟对象进行 spy/restore，避免副作用影响其他测试
- 调试技巧
  - 在 jest.setup.js 注入必要 polyfill，减少环境差异
  - 使用 console.log 或断点定位事件触发时机
  - 对 WebSocket 与 HTTP 信令分别断言消息字段与数量

章节来源
- [jest.setup.js:1-35](file://client/jest.setup.js#L1-L35)
- [signaling.test.js:1-485](file://client/test/signaling.test.js#L1-L485)
- [peerconnection.test.js:1-251](file://client/test/peerconnection.test.js#L1-L251)