29、端到端测试:使用 Selenium 和 WebRTC 测试框架编写端到端测试用例
端到端测试,说白了就是模拟真实用户的操作流程。你想想看,用户打开浏览器,点开你的视频通话页面,然后开始聊天——这一整套流程,我们得用代码自动跑一遍。
我刚开始做WebRTC测试时,总觉得单元测试够了。直到有一次上线后,发现两个Chrome版本之间居然连不上。嗯,从那以后,我再也不敢跳过端到端测试了。
为什么需要端到端测试?
WebRTC涉及的东西太多了。信令服务器、STUN/TURN、编解码器、网络协商……每个环节都可能出问题。单元测试只能测单个模块,集成测试能测模块间的交互,但只有端到端测试,才能验证整个系统在真实浏览器环境下的表现。
我个人习惯把端到端测试分成三层:
- 基础连通性测试:两个浏览器能不能建立连接
- 媒体流测试:音视频能不能正常传输
- 异常场景测试:断网、切换设备、多人通话等
今天我们就重点讲第一层——用Selenium控制浏览器,配合WebRTC测试框架,写一个完整的端到端测试用例。
技术选型
做WebRTC端到端测试,常用的工具有这些:
| 工具 | 用途 | 我的评价 |
|---|---|---|
| Selenium WebDriver | 控制浏览器 | 稳定,社区大,必选 |
| puppeteer | 控制Chrome | 轻量,但只支持Chromium |
| karma-webrtc | WebRTC专用测试框架 | 适合单元测试,端到端不够灵活 |
| testRTC | 商业WebRTC测试平台 | 功能全,但收费 |
我推荐用 Selenium + 自定义WebRTC辅助库 的组合。Selenium负责控制浏览器,我们自己写一些辅助函数来操作WebRTC API。
核心架构
先看一张整体流程图,帮你理解测试是怎么跑起来的:
环境准备
先把依赖装上。我用的是 Node.js + Mocha + Selenium WebDriver:
npm install selenium-webdriver mocha chai --save-dev
还需要下载对应浏览器的驱动。Chrome用chromedriver,Firefox用geckodriver。我个人习惯用Chrome,因为DevTools对WebRTC的支持更好。
编写测试用例
好,开始写代码。我们先写一个最简单的测试:两个浏览器建立连接。
const { Builder, By, until } = require('selenium-webdriver');
const { expect } = require('chai');
describe('WebRTC 端到端测试', function() {
// 设置超时时间,WebRTC连接可能需要几秒
this.timeout(30000);
let driverA, driverB;
before(async function() {
// 启动两个浏览器实例
driverA = await new Builder().forBrowser('chrome').build();
driverB = await new Builder().forBrowser('chrome').build();
// 打开测试页面
await driverA.get('http://localhost:3000/room/1');
await driverB.get('http://localhost:3000/room/1');
});
after(async function() {
await driverA.quit();
await driverB.quit();
});
it('两个浏览器应成功建立 WebRTC 连接', async function() {
// 等待连接状态变为 connected
const statusA = await driverA.wait(
until.elementLocated(By.id('connection-status')),
10000
);
const textA = await statusA.getText();
expect(textA).to.equal('connected');
const statusB = await driverB.wait(
until.elementLocated(By.id('connection-status')),
10000
);
const textB = await statusB.getText();
expect(textB).to.equal('connected');
});
});
这段代码看起来简单,但实际跑起来坑不少。我曾经遇到过一个问题:两个浏览器打开同一个房间,但就是连不上。后来发现是信令服务器的WebSocket连接没处理好,第二个浏览器加入时没触发重新协商。
深入测试:验证媒体流
光看连接状态还不够。连接上了,但有没有视频流?这才是关键。
我一般会在测试页面里加一个辅助函数,用来检查视频元素是否有实际数据:
// 在测试页面中注入的辅助函数
window.checkVideoPlaying = function(videoElement) {
return new Promise((resolve) => {
if (videoElement.readyState >= 2) {
resolve(true);
return;
}
videoElement.addEventListener('canplay', () => resolve(true));
// 超时处理
setTimeout(() => resolve(false), 5000);
});
};
然后在测试脚本里调用:
it('视频流应正常播放', async function() {
// 在浏览器A中执行检查
const isPlayingA = await driverA.executeScript(
'return window.checkVideoPlaying(document.getElementById("remote-video"));'
);
expect(isPlayingA).to.be.true;
// 在浏览器B中执行检查
const isPlayingB = await driverB.executeScript(
'return window.checkVideoPlaying(document.getElementById("remote-video"));'
);
expect(isPlayingB).to.be.true;
});
--use-fake-device-for-media-stream 就能搞定。
处理 ICE 候选
WebRTC 连接过程中,ICE 候选的收集和交换是个关键环节。我习惯在测试中暴露 ICE 状态,方便断言:
it('ICE 连接状态应为 connected', async function() {
const iceStateA = await driverA.executeScript(
'return window.peerConnection.iceConnectionState;'
);
expect(iceStateA).to.equal('connected');
const iceStateB = await driverB.executeScript(
'return window.peerConnection.iceConnectionState;'
);
expect(iceStateB).to.equal('connected');
});
你想想看,如果 ICE 状态一直是 checking 或者 failed,那肯定是网络或者 STUN/TURN 配置有问题。我在项目中遇到过好几次,都是因为 TURN 服务器地址写错了。
模拟网络变化
端到端测试不能只测理想情况。真实场景下,网络会抖动、会断连。Selenium 可以配合 Chrome DevTools Protocol 来模拟网络条件:
it('网络切换时应能重新连接', async function() {
// 模拟网络断开
await driverA.executeCdpCommand('Network.emulateNetworkConditions', {
offline: true,
latency: 0,
downloadThroughput: 0,
uploadThroughput: 0
});
// 等待几秒
await driverA.sleep(3000);
// 恢复网络
await driverA.executeCdpCommand('Network.emulateNetworkConditions', {
offline: false,
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1
});
// 验证重新连接
const iceState = await driverA.wait(
async () => {
const state = await driverA.executeScript(
'return window.peerConnection.iceConnectionState;'
);
return state === 'connected' ? state : null;
},
15000,
'连接未恢复'
);
expect(iceState).to.equal('connected');
});
测试报告与日志
测试跑完了,结果怎么看?我一般会做两件事:
- 截图:在关键步骤截图,比如连接建立后、视频播放后。Selenium 的
takeScreenshot()方法就能搞定。 - 抓取 WebRTC 统计信息:通过
getStats()API 获取连接质量数据,写入日志文件。
// 截图示例
await driverA.takeScreenshot().then((data) => {
require('fs').writeFileSync('screenshot.png', data, 'base64');
});
// 获取统计信息
const stats = await driverA.executeScript(
'return window.peerConnection.getStats(null).then(r => { let res = {}; r.forEach(s => { res[s.type] = s; }); return res; });'
);
console.log('WebRTC Stats:', JSON.stringify(stats, null, 2));
说实话,WebRTC 的端到端测试确实比普通 Web 应用要复杂。但只要你把基础框架搭好,后面加测试用例就轻松多了。我个人习惯是先写一个最小可用的测试,跑通了再慢慢加各种场景。
好了,今天就聊到这里。记住一点:端到端测试不是银弹,但它能帮你抓住那些单元测试和集成测试漏掉的真实环境问题。下次遇到用户说「视频连不上」,你先跑一遍端到端测试,八成能找到原因。
公众号:蓝海资料掘金营,微信deep3321