27、屏幕共享测试:单元测试、端到端测试、模拟屏幕流、自动化测试框架
屏幕共享这个功能,写起来挺爽,测起来是真头疼。
我记得第一次给客户演示屏幕共享,结果一共享就黑屏,当时那个尴尬啊……后来我学乖了,测试必须前置,而且得分层测。今天我就把这几年的测试心得掰开揉碎讲给你听。
为什么屏幕共享测试这么特殊?
说白了,屏幕共享涉及的东西太多了。采集、编码、传输、解码、渲染,哪一环出问题都不行。而且它不像普通视频通话,屏幕内容变化快、分辨率高、帧率要求也高。
你想想看,普通摄像头30帧就够了,屏幕共享有时候得60帧,不然鼠标移动都卡顿。这就对测试提出了更高的要求。
核心观点:屏幕共享测试必须分层进行,从最小的单元测试,到集成测试,再到端到端测试,每一层都不能跳过。
单元测试:把每个零件单独测
单元测试,就是测试最小的代码单元。比如一个编码器初始化函数、一个分辨率切换逻辑、一个帧率计算器。
我个人习惯用 Jest 配合 sinon 来做。为什么?因为屏幕共享涉及很多浏览器 API,比如 getDisplayMedia,这些在 Node 环境里没有,必须 mock 掉。
// 测试分辨率切换逻辑
describe('ResolutionManager', () => {
it('should switch to lower resolution when CPU overload', () => {
const manager = new ResolutionManager();
manager.setCurrentCPUUsage(85); // 模拟CPU高负载
const result = manager.getTargetResolution();
expect(result.width).toBe(1280); // 期望降级到720p
expect(result.height).toBe(720);
});
it('should keep high resolution when idle', () => {
const manager = new ResolutionManager();
manager.setCurrentCPUUsage(30);
const result = manager.getTargetResolution();
expect(result.width).toBe(1920);
});
});
我的经验:单元测试里最难测的是时间相关的逻辑。比如帧率控制、超时重连。我一般会用 sinon 的 fake timers 来模拟时间流逝,这样测试跑起来飞快,不用真的等几秒钟。
模拟屏幕流:没有真屏幕也能测
做自动化测试的时候,总不能每次都开个真屏幕吧?那太慢了。我们需要模拟屏幕流。
我常用的方案有两种:
- Canvas 模拟流:用 Canvas 画一些动态内容,比如移动的方块、变化的数字,然后通过
captureStream()转成 MediaStream。 - 预录视频模拟:用一段预录的屏幕录制视频,通过
HTMLVideoElement播放并 captureStream。
// 用 Canvas 模拟屏幕流
function createMockScreenStream(width = 1920, height = 1080) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 画一个移动的方块,模拟屏幕变化
let x = 0;
function drawFrame() {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = 'blue';
ctx.fillRect(x, 100, 200, 200);
x = (x + 5) % width;
requestAnimationFrame(drawFrame);
}
drawFrame();
return canvas.captureStream(30); // 30fps
}
注意:Canvas 模拟流虽然方便,但它没有真实的屏幕内容变化模式。比如鼠标移动、滚动页面这些细节,Canvas 模拟不出来。所以它适合做性能测试和稳定性测试,不适合做画质测试。
端到端测试:模拟真实用户操作
端到端测试,就是模拟两个用户真实地建立连接、共享屏幕、接收屏幕流。这个环节我推荐用 Puppeteer 或者 Playwright。
为什么?因为它们可以控制真实的 Chrome 浏览器,能调用 getDisplayMedia。不过有个坑——getDisplayMedia 会弹出系统级的选择窗口,这个 Puppeteer 默认搞不定。
我的解决方案是这样的:
- 在测试环境中,用 Chrome 的
--auto-select-desktop-capture-source启动参数,自动选择屏幕源。 - 或者,在代码里加一个测试开关,当检测到测试环境时,用模拟流替代真实采集。
// Playwright 端到端测试示例
test('screen sharing should work end-to-end', async ({ browser }) => {
const context1 = await browser.newContext();
const context2 = await browser.newContext();
const page1 = await context1.newPage();
const page2 = await context2.newPage();
// 页面1发起屏幕共享
await page1.goto('https://your-app.com/room/123');
await page1.click('#start-sharing');
// 页面2加入同一房间
await page2.goto('https://your-app.com/room/123');
// 验证页面2收到了屏幕流
const videoElement = await page2.waitForSelector('#remote-screen video');
const hasVideo = await videoElement.evaluate(el => el.srcObject !== null);
expect(hasVideo).toBe(true);
});
自动化测试框架:把测试跑起来
单元测试、模拟流、端到端测试都写好了,怎么把它们组织起来?我建议用这样的分层架构:
这个架构的好处是:
- 单元测试跑得最快,每次提交代码都能跑,几分钟就出结果。
- 模拟流测试稍慢,但不需要真实屏幕,可以在 CI 环境里跑。
- 端到端测试最慢,但最接近真实场景,适合在合并代码前跑一次。
我的建议:在 CI 里,把单元测试和模拟流测试设为必须通过的门禁。端到端测试可以设为定时任务,比如每天凌晨跑一次。这样既保证了质量,又不会拖慢开发节奏。
避坑指南:我曾经踩过的坑
做屏幕共享测试这几年,我踩过不少坑,挑几个典型的说说:
- 分辨率不匹配:我曾经遇到过,发送端采集的是 4K 分辨率,接收端解码后显示不出来。后来发现是接收端的解码器不支持 4K。解决方案是在测试里加入分辨率协商的验证。
- 帧率波动:屏幕共享的帧率波动比摄像头大得多。我见过从 60 帧突然掉到 5 帧的情况。后来我在测试里加入了帧率监控,低于阈值就报警。
- 内存泄漏:屏幕共享如果长时间运行,内存会不断增长。我写过一个测试,让屏幕共享跑 24 小时,监控内存变化。结果发现每 10 分钟泄漏 2MB,跑一天就泄漏了 288MB。
特别提醒:屏幕共享测试一定要考虑多显示器场景。很多用户有双屏甚至三屏,共享其中一个屏幕时,采集逻辑和坐标计算都可能出问题。我建议在测试矩阵里加入多显示器模拟。
总结一下
屏幕共享测试,说白了就是三个字:分层测。单元测试保基础,模拟流测试保性能,端到端测试保体验。每一层都有它的价值,缺一不可。
我个人习惯把测试用例写成表格,方便追踪覆盖情况:
| 测试类型 | 覆盖内容 | 运行频率 | 工具 |
|---|---|---|---|
| 单元测试 | 编码器、分辨率管理、帧率控制 | 每次提交 | Jest + sinon |
| 模拟流测试 | 不同分辨率、帧率、内容变化模式 | 每次提交 | Canvas captureStream |
| 端到端测试 | 真实共享、接收、渲染流程 | 每日定时 | Playwright |
| 压力测试 | 长时间运行、多用户并发 | 每周一次 | 自定义脚本 |
嗯,这套测试体系我用了两年多,帮我在上线前抓到了不少问题。希望你也能用上,少走一些弯路。