自动化测试框架怎么选?Selenium、Playwright、Cypress 两年踩坑后的真实对比

🔑 关键词:自动化测试,Playwright,Selenium,Cypress,测试框架选型

📖 摘要:不是罗列 API 的选型文。从失败可信度、排查成本、单次执行时长三个维度,对比 Selenium 4 / Cypress / Playwright 的真实差异,附实测耗时数据和几个反常识的踩坑结论。

上个月又有人问我,新项目自动化测试选 Playwright 还是 Cypress。这问题我 2021 年就被问过,当时的答案和现在不一样了,所以我一般先反问一句:你们的 CI 跑一次多久?

图片

我第一次认真搞 E2E 是在一家做 SaaS 后台的公司,2019 年。那套 Selenium Grid 上跑着 800 多个用例,三层 Page Object 写得规规矩矩,但从头到尾跑一遍要 42 分钟,还是顺利的情况下。挂掉的用例里大概三分之一重跑一次就过——也就是 flaky。后来我做了一件很不「最佳实践」的事:把其中 400 多个用例删了,只留核心链路。跑的时间降到 11 分钟,而线上出的 P0 级 bug 数量,没变。

这段经历改变了我判断框架的标准。大部分选型文章在比 API 优雅度、比文档写得好不好,但真正决定你半年后是被这个框架救了还是被它拖死的,是三件事:失败能不能信、失败能不能查、单次跑多久。

先把三家的底细摊开

图片

Selenium:2004 年 Jason Huggins 在 ThoughtWorks 内部做出来的,现在挂在 Software Freedom Conservancy 下面。它最大的资产是 W3C WebDriver 成了标准——Chrome 驱动、GeckoDriver、SafariDriver 都得按这套协议说话,所以你换语言(Java/Python/C#/Ruby/JS/Kotlin)都能用。代价是抽象层太薄,你得自己写显式等待(WebDriverWait 配 expected_conditions),自己处理 stale element 异常,页面上一个 loading 遮罩没消失,它就真敢给你抛异常。

Cypress:2015 年 Brian Mann 开始做。它最大的特点是测试代码跑在被测页面同一个浏览器事件循环里,所以能直接摸 window、直接操作 DOM,调试体验确实是三家里最好的,改一行代码浏览器自动重跑。但这也意味着你的测试跑在一个 iframe 里,多标签页、跨域、原生文件下载这些场景天生别扭——cy.origin() 是 9.6 版本才加的(2021 年 4 月),而且到现在还有些 cookie 和第三方脚本的边角情况处理不掉。另外它只跑 Chromium 系和 Firefox,不跑 WebKit,也就是说你测不了 Safari 的真实行为,只能靠「差不多吧」安慰自己。

Playwright:微软 2020 年 1 月开源,主创是从 Puppeteer 团队出来的。它跟 Cypress 正好相反,跑在浏览器外面,通过 CDP 和 WebKit 的私有协议控制。换来的能力是:真多标签页、真跨域、真文件下载、context 隔离(一个 browser 起 N 个 context,各自带独立的 cookie 和 storage),以及 Trace Viewer——它会把每一步的 DOM 快照、网络请求、console 日志全录下来,失败之后直接拖时间轴回放,不用再猜「它当时到底看到的是哪个页面」。

图片

顺便说清一件事,Selenium 4 也用 CDP 做网络拦截和性能指标采集,但 CDP 是 Chrome 的私有协议,Firefox 那边只实现了子集,跨浏览器一致性会打折扣。这是个很容易被忽略的坑。

一张表,以及几个我实际量过的数字

维度 Selenium 4 Cypress Playwright
浏览器内核 Chrome/Firefox/Safari/Edge,走 W3C WebDriver Chromium 系 + Firefox,不跑 WebKit Chromium/Firefox/WebKit 三内核
运行位置 进程外,通过 driver 通信 被测页面同一个事件循环里 进程外,CDP + WebKit 私有协议
多标签/跨域 支持,但要自己管窗口句柄 cy.origin() 之后好些,边角仍多 原生支持
并行方式 Grid 4 自建,成本在运维 Cypress Cloud 按并行数收费 命令行 --shard=1/4,免费
失败排查 靠日志 + 截图,自己加 时间旅行调试,体验最好 Trace Viewer,DOM 快照 + 网络 + console
语言绑定 Java/Python/C#/Ruby/JS/Kotlin 全 只有 JavaScript/TypeScript JS/TS、Python、Java、.NET

图片

数字部分,机器是 8 核 16G 的 CI runner,同一套 300 个左右的登录 + 下单 + 改设置用例:

  • Playwright(Chromium,4 workers)跑完 6 分半;Cypress(4 个并行容器)9 分钟出头;老的 Selenium Grid 串行 23 分钟。差距主要来自 context 复用和启动开销,不是执行速度。
  • Playwright 那套自动等待(actionability check 是 visible / stable / receives events / enabled / editable 五道)之后,跟等待相关的 flaky 从原来 Selenium 里的 11% 掉到 2% 以下。剩下的 2% 基本都是测试数据自己打自己——两个用例同时改同一个用户的昵称,这种跟框架一点关系都没有,你就是换十个框架也治不好。
  • Trace Viewer 的 zip,一个 30 步的用例大概 2 到 5MB。所以别全量开 trace: 'on',用 trace: 'on-first-retry' 划算得多,否则 CI 产物几天就能涨到几个 G,还得回头去配 artifact 保留天数。

图片

我的几个偏见,你可以不同意

别为「录制回放」付钱。 所有录制工具都只能录出 happy path,而 happy path 恰恰是最不需要测的。我见过的能长期活下来的录制方案,都是把录制当成「生成初始定位器」用,后面还是得手改,当成一次性的脚手架。指望它省掉写代码的活,三个月后你会收获一堆没人敢动的红色用例。

E2E 不该是主力。 Kent C. Dodds 2018 年那个 Testing Trophy 的说法我基本认同:静态检查 → 单元 → 集成(占比最大)→ E2E。E2E 只该覆盖「用户能完成核心业务」这条线,剩下的交给集成测试。我踩过最深的坑就是拿 E2E 去测表单校验的 12 种边界情况——慢、脆、UI 改一次修三天,纯属自虐。

图片

定位器策略比框架选型重要一百倍。 用 data-testid,别用 CSS class,更别用 XPath 里的 nth-child 或者那种七拐八绕的绝对路径。这条不管你用哪个框架都成立,而且它比选框架更能决定你半年后会不会想把整个测试仓库删了重写。我见过最离谱的一个定位器,是从 body 往下数了 14 层再配一个 :nth-child(3),前端加了个 banner 就全挂了。

Playwright 不是无脑最优解。 如果你们团队全是 Java 系、项目是内网企业系统、Selenium + JUnit 5 生态成熟招人也好招,真没必要赶时髦。Playwright 的 Java 绑定现在也齐了,但社区里 Python 和 TypeScript 的示例数量差着一个量级,遇到怪问题搜不到答案的时候,那种孤独感你得有心理准备。

最后一个真话:自动化测试的 ROI 是有拐点的。用例数到了 300 到 500 这个区间之后,维护成本涨得比收益快,除非你同时把测试数据管理和页面对象分层做扎实。我现在的做法很土,每个 E2E 用例都问自己一句「如果它挂了,我今晚会不会爬起来改代码」,答案是不会的,就删掉。删完你会发现自己睡得更好了。

🏷️ 标签: