Selenium太痛?8款替代工具横评:Playwright、Cypress、Puppeteer如何选
2026/9/6 8:36:36 网站建设 项目流程

先交代清楚一个问题:我并不是说 Selenium 已经过时了。做了这么多年自动化,我手头相当一部分线上项目到现在还是用 Selenium 在跑,稳定性和社区资料都有保障。但如果你和我一样,经历过“Chrome 自动升级之后全部脚本崩掉”“find_element 加显式等待写到手软”“CI 上跑一套 200 条的用例要 40 分钟”这种处境,你大概也会开始认真看替代方案。

Selenium 的生态地位不用怀疑,可它的体验停留在“能用”,而现在的自动化需求已经是“省心”。这篇内容不是让你彻底告别 Selenium,而是把 8 款实战中真正能顶替它、甚至更省事的工具按场景拆清楚。无论你是做 Web UI 自动化测试、爬虫脚本,还是内部运营工具的页面操作,都应该能找到比自己现在这套更顺手的方案。

1. Selenium 的痛点:不是工具不好,而是时代变了

Selenium 能撑到今天,说明它的设计在 Web 早期是成立的。问题是:今天的网页已经不是当年那个页面了,前端框架、微服务架构、动态渲染、复杂交互,全都变了,自动化工具的思路却还停留在“驱动浏览器 + 找元素 + 操作元素”这条老路上。

1.1 真正的障碍在哪

我总结下来,最折磨人的其实就四类问题。

驱动管理和浏览器版本捆绑。这是 Selenium 用户绕不过去的第一道坎,chromedriver、geckodriver 都有严格的对齐要求。Chrome 一升级,本地脚本立刻罢工,CI 上如果镜像里的浏览器版本没锁住,问题更明显。现在 Selenium 有了 Selenium Manager 能自动管理部分驱动,但你在直接上手新项目时,体验还是不如那些把浏览器下载、版本锁定一并做掉的现代工具。

等待机制全靠自己写。Selenium 的显式等待本身是合理的,但每个元素都要 WebDriverWait,稍微复杂一点的页面,一个用例里能出现十几个 EC 条件代码块。新手最常见的做法是把隐式等待调成 10 秒、20 秒来掩盖网络波动,结果整个测试套件肉眼可见地变慢。慢还不是最可怕的,隐式等待和显式等待混用造成的随机超时,才是 CI 上最经典的玄学问题。

定位元素绕不过 CSS/XPath,还特别脆。现代前端经常把 class 名语义化得很弱,今天btn-submit,明天重构就变btn_cus_123。项目规模一大,维护定位器的成本已经不亚于维护业务代码本身。你要靠 XPath 去定位页面里某个文本内容,更是每动一次页面结构都要跟着改。

浏览器能力覆盖不全。如果你做的是那种带下载、上传、多标签页、shadow DOM、网络请求拦截的自动化,Selenium 能实现,但实现方式绕得很。比如 CDP 相关的底层操作,在 Selenium 里支持的完整度一直不如协议级工具来得干净。

1.2 Selenium 为什么还活着

它有一个巨大的护城河:生态成熟。文档、Stack Overflow 问答、各语言 binding、Appium 移动端复用同一套 WebDriver 协议、和 Jenkins/Azure DevOps 等 CI 系统的整合文档到处都是。团队新增一个人的时候,招来的测试开发基本都用过 Selenium,学习成本几乎为零。

在我接触过的不少企业里,已有的自动化资产往往就是 Selenium 代码库。不是说换就换的。这也是为什么后面介绍的工具里,我会特意保留一条“和现有 WebDriver 体系兼容”的路线——你完全可以用 WebdriverIO 或 Nightwatch 这样的工具逐渐替代旧脚本,而不是推翻重来。

1.3 什么情况确实该换

我给一个很简单的判断标准:如果在你的工作流里,“让脚本稳定跑起来”这件事消耗的时间超过了“写业务操作”本身,你就应该考虑换了。

还有一种情况是:你只是想做一次爬虫或一次性的页面查询,结果光写 driver 环境配置、等待函数、异常重试就花了半天。这种短期任务用 Selenium 明显是在给自己挖坑。新工具普遍把浏览器安装、自动等待、页面交互封装得更彻底,你一上手就是写业务,不用先处理那些前置逻辑。

2. 两条主流进化路线:Playwright 与 Puppeteer

这一节我放在最前面,是因为它们代表了目前网页自动化的主流进化方向:直接通过浏览器 DevTools 协议(CDP)与浏览器底层通信,而不是走 HTTP WebDriver 服务。速度和稳定性都是代际级别的提升。

2.1 Playwright:一站式跨浏览器自动化

如果说现在只让我推荐一个 Selenium 替代工具,我会说 Playwright。它由微软团队维护,目前在 GitHub 的活跃度、文档完善度、社区口碑都属于第一梯队。

真正的杀手锏有三个。

第一,自动等待。Playwright 内置了可执行性检查,你在点击一个按钮之前,它会自动判断这个元素是否可见、是否被遮挡、是否可交互、是否处于稳定状态,满足条件才会执行点击。这些检查不是盲目 sleep,也不是反复扫页面,而是轮询判断,且有超时上限。我做过对比,同一个支付流程用例,Selenium 里我写了 7 次 WebDriverWait,Playwright 里一个 wait 方法都没写,跑下来比 Selenium 的用例更快、更稳。

第二,浏览器管理器。一条命令npx playwright install chromium,它会自动下载匹配的浏览器内核并锁定版本,你的脚本不再依赖系统升级后你第一时间去补版本。它同时支持 Chromium、Firefox、WebKit,同一套脚本可以平行跑三种内核。

第三,生态闭合成环。Codegen 录制脚本、UI Mode 调试、Trace Viewer 回放全部内置。你在代码里加一个--trace on,跑完之后打开 trace 文件就能看到每一步的页面截图、网络请求、DOM 快照和 console 日志。Selenium 里你得额外集成 Allure 或者自研上报才能达到这种调试体验。

用 Python 也非常顺。Selenium 的 Python 用户切过来基本上没有心理负担:

from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.goto("https://example.com") page.get_by_role("button", name="登录").click() page.get_by_label("用户名").fill("test") page.screenshot(path="login.png") browser.close()

它和 Selenium 最大的区别就是定位器。

# Selenium 时代 driver.find_element(By.CSS_SELECTOR, "#login-btn").click() # Playwright 时代 page.get_by_role("button", name="登录").click()

角色定位器的好处是:你不再依赖前端必须保留某个 class 名,只要按钮的语义角色和可访问名称不变,页面重构也不影响脚本。这本质上是让自动化测试代码和前端的耦合度下降了一大截。

当然也有劝退点:它对测试架构是有要求的,组件化、夹具(fixture)、并发隔离都需要你去理解它的 runner 模型。如果你只想要“快速跑一条冒烟用例”,它要比 Selenium 多一点框架感。

2.2 Puppeteer:Chrome 生态里的轻骑手

Puppeteer 是 Google 官方出的 Node.js 库,主打 Chrome/Chromium 控制。它解决的问题更聚焦:给你的 Node 应用一个完整的浏览器自动化能力。

Puppeteer 的定位不是“测试框架”,它没有内置断言和 test runner。它的强项是页面抓取、页面渲染、生成 PDF、性能分析这一类“我要在一个无头浏览器里做事情”的场景。

比如我要批量抓取一组页面并生成淘宝风格的商品快照,Selenium 会先启动 webdriver、再启动浏览器、每个 URL 都要重新写等待逻辑;Puppeteer 的代码则非常直接:

const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com', { waitUntil: 'networkidle0' }); await page.click('#accept'); await page.screenshot({ path: 'page.png', fullPage: true }); await browser.close(); })();

waitUntil: 'networkidle0'这个参数就很能说明 Puppeteer 的取向——它关注的是页面状态是否稳定,而不是简单判断某个元素存不存在。只要你清楚自己要抓取的目标 URL 列表,这套链路写起来非常顺。

但它的局限也明显:默认只支持 Chromium 系列,Firefox 支持是实验性的。如果你需要跨浏览器覆盖率来验证兼容性,Puppeteer 不是最优解,Playwright 才是。我的建议是:如果你的目标是“自动化操作 Chrome 完成特定任务”,选 Puppeteer;如果你的目标是“团队级 Web 自动化测试”,直接上 Playwright。

3. 前端与全链路视角:Cypress 与 TestCafe

如果说 Playwright/Puppeteer 是“浏览器协议派”,那 Cypress 和 TestCafe 就是“换个跑法”的典型代表。它们不再站在浏览器外部发指令,而是直接插进前端运行环境里,很多同步问题从根上就消失了。

3.1 Cypress:测试代码写起来像前端代码

Cypress 的架构和 Selenium 完全不是一个生态。它的测试代码和被测应用跑在同一个浏览器进程里,所以它天然知道页面内部发生的很多事情。

这种架构最直接的好处是:再也不会出现“元素已经在页面上,但脚本点击没反应”这种 Selenium 常见问题。Cypress 内置了相似的自动重试机制,命令执行前会确认元素可交互,且默认快照、录像、可调试,前端开发者上手几乎没有陌生感。

它的 API 是这样的:

describe('登录流程', () => { beforeEach(() => { cy.visit('/login'); }); it('应该能正常登录', () => { cy.intercept('POST', '/api/login').as('loginRequest'); cy.get('#username').type('testuser'); cy.get('#password').type('123456'); cy.get('button[type=submit]').click(); cy.wait('@loginRequest'); cy.contains('欢迎回来').should('be.visible'); }); });

cy.intercept直接拦截网络请求,这和 Selenium 里要借助代理才能实现同样的功能相比,根本不是一个时代的体验。你可以在测试里 mock 接口返回、延迟响应、断言请求体,不需要额外起一个网管或者搭一层代理。

但 Cypress 有个硬伤:它只支持在自己的环境里打开浏览器并跑测试,无法控制浏览器原生的多标签页逻辑,跨域 iframe 和某些企业级登录方案(比如需要打开额外窗口)支持起来都很别扭。如果你做的是后台管理系统、内部 OA 这类相对单一的前端应用,Cypress 体验极佳;如果做的是涉及大量第三方站点跳转、复杂窗口管理的流程,它就不是最合适的。

3.2 TestCafe:还不想要浏览器驱动?试试这套

TestCafe 走的是另一条技术路线:它通过一个代理服务器来操纵浏览器,不需要任何浏览器驱动,也不需要安装插件。这一点在限制严格的企业内网环境里价值巨大。

它的 API 简洁到没什么存在感:

import { Selector } from 'testcafe'; fixture('登录测试') .page('https://example.com/login'); test('验证登录提示', async (t) => { await t .typeText('#username', 'test') .typeText('#password', 'password') .click('#login-btn') .expect(Selector('.welcome').innerText).eql('欢迎回来'); });

TestCafe 会在页面加载后自动等待元素可用。它不像 Selenium 那样让你纠结“到底用显式等待还是隐式等待”,大部分情况下它就是能等,等到了才执行下一步。

它支持桌面浏览器、移动端远程浏览器以及各种云测试平台,且测试代码本身不绑定浏览器环境。这意味着你可以用同一套测试用例在本地、CI、SauceLabs、BrowserStack 上跑。

这里需要提醒一下:TestCafe 的发展方向有过几次调整,授权策略也曾变化,你在把它引进团队之前,要去官网确认最新版本的 license 和服务支持范围。它依然是一个好工具,但“免费开源到永远”不一定成立。

4. 靠 WebDriver 起飞的老熟人:WebdriverIO 和 Nightwatch.js

有些人看到上面这些新框架,第一反应不是“真香”,而是“我们已经有一堆 Selenium 资产了,换不起”。如果你有这种顾虑,WebdriverIO 和 Nightwatch.js 就是为你准备的。

4.1 WebdriverIO:从 Selenium 无缝切过来的首选

WebdriverIO 本质上还运行在 WebDriver 协议之上,但你完全可以把它理解成“重新设计过的 Selenium”。它依旧能复用 Selenium Grid、能接入 Appium 做移动端、能对接你现有的各种 infra,但开发体验完全重做了。

它最大的变化是把异步语法和测试 runner 做进了框架。你不再需要自己封装 driver factory、等待工具类、报告生成器,WebdriverIO 都帮你内置好了:

import { browser, expect } from '@wdio/globals'; describe('购物车流程', () => { it('应该能添加商品', async () => { await browser.url('https://example.com'); const addButton = await $('#add-to-cart'); await addButton.waitForDisplayed({ timeout: 5000 }); await addButton.click(); await expect($('.cart-count')).toHaveText('1'); }); });

和 Selenium 最大的区别是什么?它有一整套命令链和钩子机制,你不需要每次都要现写WebDriverWait,而是像waitForDisplayed这种链式方法直接用。测试报告、Allure 集成、视觉回归对比、Spec 测试报告等都是官方或社区插件,装一个包就能用,不像 Selenium 要把一堆第三方依赖手工拼起来。

不过要注意,WebdriverIO 从 v8 开始全面转向 async/await 模式。如果你在网上看教程看到早期版本的browser.clickbrowser.setValue同步写法,那是老版本,别照着抄。现代 WebdriverIO 的每条命令都是 Promise,你要么全部 await,要么配合自定义重试包装器。

4.2 Nightwatch.js:配置驱动、贴近旧习惯

Nightwatch.js 也保留 WebDriver 协议,但它更进一步,把配置驱动思想贯彻得比较彻底。你可以把它理解成“长得像 Selenium 的测试框架 + 一个内置的 webdriver 服务管理器”。

它最吸引人的地方是结构清晰。你只需要一个.nightwatch.json配置文件,指定浏览器、测试目录,剩下的事情框架来处理:

{ "src_folders": ["test/e2e"], "webdriver": { "start_process": true, "server_path": "node_modules/.bin/chromedriver" }, "test_settings": { "default": { "desiredCapabilities": { "browserName": "chrome" } } } }

写测试用例也很简洁:

describe('首页冒烟测试', function() { it('验证标题', function(browser) { browser .url('https://example.com') .assert.titleContains('Example') .end(); }); });

看到这个结构和 Selenium 时代 JUnit/TestNG 代码相似,老测试开发几乎没有学习成本。甚至可以把现有 Selenium 测试用例逐步搬过来,用相同的 WebDriver 能力,但统一了框架层。

需要客观说的是,Nightwatch 的生态规模、插件丰富度不如 WebdriverIO 和 Playwright。如果你需要非常复杂的测试编排,比如多平台并发、复杂的超时策略、自研一步断言,那它稍微有点力不从心。它更适合中小团队、结构简单的 Web 项目。

5. Taiko 与 Katalon:给不想写选择器的人的第二条路

如果你已经开始厌倦 CSS 选择器、XPath、各种定位策略的维护,这个分类里的思路会让你眼前一亮:它们把“用什么选择器”这件事尽量消解掉。

5.1 Taiko:让脚本口语化

Taiko 是 ThoughtWorks 开源的自动化库,它最大的特点就是“你像跟人类说话一样告诉浏览器做什么”。

const { open, goto, write, click, close, text } = require('taiko'); (async () => { try { await open('browser'); await goto('https://example.com/login'); await write('testuser', into(textBox({ placeholder: '用户名' }))); await write('password', into(textBox({ placeholder: '密码' }))); await click(button('登录')); await assert.text('欢迎回来').exists(); } catch (error) { console.error(error); } finally { await close(); } })();

注意看这里的选择器:textBox({ placeholder: '用户名' })button('登录')text('欢迎回来')。它通过无障碍树和实际可见文本来定位元素,而不是依靠 class 名或 XPath。这意味着前端的 class 怎么改,只要页面上还显示“用户名”这个提示词,脚本就不用动。

Taiko 最大的价值在于自动化脚本的可读性。你甚至可以把它当作文档用,产品经理、业务人员也能看懂这个脚本在做什么。这一点在团队协作和审计场景下优势明显。

它同样基于 CDP 与浏览器通信,速度和稳定性都有保障,和 Gauge 这个 BDD 框架配合尤其好。缺点是它主要照顾 Node.js 生态,且社区资料相对少,遇到冷门问题你可能要自己去翻源码。

5.2 Katalon:低代码与团队协作

Katalon Studio 不是纯编码工具,它的定位是“测试平台”。你可以手动写脚本,但更常见的工作流是录制回放、关键字驱动、和 BDD 描述。

Katalon 特别适合两类场景:一类是团队里有不擅长编程的测试人员,想把业务人员带入自动化流;另一类是需要管理和跟踪大量测试用例的企业环境。它有测试套件管理、执行历史、测试报告、与 JIRA 集成,基本就是一套开箱即用的测试管理系统。

它的操作方式一般是:打开 Record 功能,人工操作一遍被测系统,Katalon 自动录制关键字,然后你可以调整测试数据、加上断言,再放进 CI 跑。整个过程不需要手写一行 CSS/XPath。

需要提醒一句:Katalon 有免费和商业授权之分,商业团队使用时一定要去确认清楚版本条款,特别是把免费版用于企业项目时,授权边界需要弄明白。低代码工具经常会踩这种坑,签合同或合规审查时不太好看。

6. 八大工具横评与选型参考

到了需要拍板的时候,光看单个工具的优点没有意义,还是要放在你自己的场景里比。

6.1 横评对照表

工具底层技术自动等待多浏览器主要语言最适合场景上手难度
PlaywrightCDP原生完整Chromium/Firefox/WebKitJavaScript/TypeScript/Python团队级 Web UI 自动化中等
PuppeteerCDP基础支持Chromium 为主JavaScript爬虫、页面处理简单
Cypress进程内驱动原生完整仅浏览器环境JavaScript/TypeScript前端应用端到端测试简单
TestCafe代理服务器原生完整多浏览器JavaScript/TypeScript内网环境、浏览器兼容简单
WebdriverIOWebDriver / CDP显式+隐式多浏览器JavaScript/TypeScript从 Selenium 迁移过渡中等
Nightwatch.jsWebDriver可配置多浏览器JavaScript结构化团队测试
TaikoCDP原生完整Chromium/EdgeJavaScript/TypeScript追求低维护的运营自动化
KatalonWebDriver 内核+平台多浏览器低代码 / Groovy低代码团队协作低(整体管理成本较高)

6.2 选型看这四件事

第一件事:团队代码栈。前端团队会用 JavaScript,那 Cypress 和 Playwright 都非常契合;后端团队用 Python,Playwright 虽然原生支持 Python,但整体生态和示例更多的还是 JS,需要做好心理建设;如果是纯测试团队且以后要维护大量用例,Katalon 这种低代码平台可降低门槛。

第二件事:你被 Selenium 的哪个痛点逼疯的。如果是等待,上 Playwright/Cypress 这类自动等待原生完整的;如果是驱动管理,选 Playwright 和 Puppeteer;如果是现有资产太重,走 WebdriverIO 逐步替换。

第三件事:跨浏览器到底是不是硬需求。我说过很多次“多浏览器”,但大部分项目实际是 Chrome-first 的,做兼容性回归可能只是每个季度手动点一轮。为了这种频率去搭建跨浏览器自动化,维护成本反而高过手工。确认不了需求时,先锁定 Chromium,跑通了再说。

第四件事:你的部署环境。内网环境、没有外网下载权限的情况下,TestCafe 这种无驱动依赖的最省事;CI 环境用 Playwright 要提前把浏览器二进制装好,不然每次全新容器都会卡在下载那一步。

7. 从 Selenium 迁移过来:最容易被忽略的差异点

很多团队选好新工具之后,其实最难的不是写新用例,而是把老用例和团队习惯迁过去。这里我踩过不少坑,给大家拆几个最关键的差异。

7.1 等待机制不是同一个物种

在 Selenium 里,你的脚本思路是:我要找这个元素,找不到就等一下,等不到就报错。几乎所有的时间都耗在“等待”上。而 Playwright/Cypress/TestCafe 这类工具的等待是内建在每一次交互里的:点击、输入、断言,都会在一个超时窗口内持续检查条件。

这带来一个迁移时的陷阱:老代码里的time.sleep(3)WebDriverWait(...).until(...)如果直接迁移过去,新工具反而会因为额外等待拖慢执行速度。迁移到 Playwright 时最正确的做法是把所有 sleep 删干净,让内置动作自己判断。

7.2 定位器思维要换

老代码里的By.IDBy.XPATH不是不能用,但你会白白丢掉新工具的价值。以 Playwright 为例,getByRole()getByLabel()这种语义化定位是它相对 Selenium 的降维优势。迁移时尽量做一次“定位器重写”,而不是把老的 XPath 原样搬过来。

有一种很实际的做法:行业规范标注。我遇到过前端负责人直接把页面元素全部加上可访问性标记,然后用 Playwright 的 role locator,测试脚本维护成本一下子下降很多。如果你想顺利换工具,最好同步推动前端做可访问性支持。

7.3 多标签页、网络拦截、调试工具全是新世界

Selenium 处理新标签页要driver.switch_to.window,Playwright 直接通过 context 管理页面,同一个会话里的多页面天然隔离。Selenium 想拦截网络请求得配置 BrowserMob 代理,而 Cypress 里一条cy.intercept就解决了,Playwright 的page.route也能做到。如果你之前没接触过这些能力,它们会颠覆你对自动化工具的认知。

7.4 现有用例的迁移顺序

务实一点,不要追求一次性全部迁移。我建议的顺序是:先挑 5 条高频、最不稳定的核心链路用例做 Pilot,用来评估新工具在你们项目里的真实体验;跑通之后再处理维护成本最高、等待逻辑最复杂的存量用例;最后才考虑拆老框架。

迁移过程中有一个容易忽略的点:CI 配置和部署环境。Playwright 的浏览器二进制要重新做镜像,Cypress 对 Node 环境版本有要求,Katalon 要在 CI 节点上装客户端。换工具从来不只是换一个依赖包,是整个基础设施链路都要跟着升级,这个成本要提前算进去。

8. 最后一点建议

工具始终是“术”,自动化体系才是“道”。我这几年换了好几轮自动化工具,最大的体会是没有一个工具能解决所有问题,但选对工具能省掉大量无意义的时间。Playwright 适合大多数新启动的项目,Cypress 适合前端驱动型的团队,WebdriverIO 适合对 Selenium 资产有依赖的过渡阶段。

如果你现在还在 Selenium 边缘徘徊,不用急着把全部历史代码推倒重来。先选一个你真正感兴趣的替代工具,拿一个平时最容易崩但不算复杂的场景练手,亲自观察一下它的自动等待、调试体验、报错信息这三个维度,相信你很快就能感受到差距。

最后提醒一句:自动化的本质是服务于业务价值,不是工具越新越好。一个稳定的 Selenium 项目,永远好过一个三天两头换框架的项目。但如果现状已经痛到影响交付了,那这 8 款工具里,一定有一款是能帮你把事情做得更省力的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询