Files
2026-05-16 13:24:02 +08:00

484 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 测试框架
<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/testJSDOM 环境下的单元与集成测试,含 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 PeeraddTrack/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)