如果说 Playwright 是“全能型选手”,那么 Cypress 就是“专为现代前端应用而生”的测试工具。它直接在浏览器中运行,提供实时重载、时间旅行调试、自动等待等特性,深受 React/Vue 开发者喜爱。本文将带你快速上手 Cypress,编写第一个端到端测试,并与 Selenium/Playwright 进行对比。
一、Cypress 是什么?
Cypress 是一个基于 JavaScript 的端到端测试框架,它在浏览器内部运行测试代码,与应用程序共享同一个执行上下文。这意味着它可以直接访问 DOM、网络请求、存储,并且能够自动等待元素和断言。
核心特点:
实时重载:修改测试代码后自动重新执行。
时间旅行:鼠标悬停在每一步,可查看当时的 DOM 快照。
自动等待:无需显式等待或 sleep,自动重试断言直到超时。
网络控制:轻松 stub 和 mock 网络请求。
截图和视频:自动记录失败用例的截图和视频。
调试友好:类似 Chrome DevTools 的调试体验。
二、Cypress 与 Selenium、Playwright 对比
结论:Cypress 非常适合 前端开发者编写组件和端到端测试,尤其是单页应用(SPA)。如果你的应用需要多标签页、跨域 iframe 或 Safari 测试,则需考虑 Playwright。
三、安装与第一个测试
3.1 安装 Cypress
mkdircypress-demo&&cdcypress-demonpminit-ynpminstallcypress --save-dev3.2 打开 Cypress 界面
npx cypressopen首次运行会创建示例测试和配置结构。
3.3 编写第一个测试
创建文件 cypress/e2e/first_test.cy.js:
describe('百度搜索测试',()=>{it('搜索 "Cypress" 应该返回结果',()=>{cy.visit('https://www.baidu.com');// 输入关键词cy.get('#kw').type('Cypress 教程');// 点击搜索按钮cy.get('#su').click();// 断言搜索结果包含关键词cy.contains('Cypress 教程').should('be.visible');});});运行:
通过界面:npx cypress open,然后点击测试文件。
命令行(无头模式):npx cypress run
四、Cypress 核心语法
4.1 命令与断言(Chai + Sinon)
Cypress 使用类似 jQuery 的链式 API,所有命令都是异步但不需要 await,内部自动处理。
// 获取元素(自动等待元素出现)cy.get('.login-form').find('input[name="username"]');cy.contains('登录');// 通过文本查找// 操作cy.get('#username').type('admin');cy.get('#password').type('123456{enter}');// 按回车cy.get('.btn').click();// 断言(使用 should,支持链式)cy.get('.welcome').should('contain','欢迎回来');cy.get('.error').should('be.visible');cy.get('h1').should('have.text','Dashboard');// 多个断言cy.get('.result').should(($el)=>{expect($el).to.have.length(3);expect($el.first()).to.contain('Cypress');});4.2 自动等待与超时
Cypress 会持续等待(默认 4 秒)直到断言成功,无需手动 sleep。
// 即使结果稍后出现,也会等待cy.get('.loading-spinner').should('not.exist');可全局或局部修改超时时间:
// 在配置文件 cypress.config.js 中defaultCommandTimeout:10000// 或在测试中cy.get('.slow-element',{timeout:15000}).should('be.visible');4.3 网络请求 Mock(Stub)
Cypress 可以拦截并修改网络请求,非常适合独立测试前端逻辑。
// 拦截 GET 请求并返回 mock 数据cy.intercept('GET','/api/users',{fixture:'users.json'}).as('getUsers');// 拦截 POST 请求并模拟响应cy.intercept('POST','/api/login',{statusCode:200,body:{token:'fake-token'}}).as('login');// 触发请求cy.get('#login').click();cy.wait('@login').its('response.statusCode').should('eq',200);4.4 处理 iframe(有限支持)
Cypress 默认不支持跨域 iframe,但可以通过加载 cypress-iframe 插件解决。
npminstall-Dcypress-iframe在 cypress/support/e2e.js 中引入:
import'cypress-iframe';使用:
cy.frameLoaded('#myIframe');cy.iframe().find('#username').type('admin');4.5 自定义命令
在 cypress/support/commands.js 中添加可复用命令:
Cypress.Commands.add('login',(username,password)=>{cy.visit('/login');cy.get('#username').type(username);cy.get('#password').type(password);cy.get('button[type="submit"]').click();cy.url().should('include','/dashboard');});测试中使用:
cy.login('admin','123456');五、测试组织与钩子
describe('用户管理模块',()=>{before(()=>{// 整个 suite 只执行一次cy.visit('/');});beforeEach(()=>{// 每个测试前执行cy.login('admin','123');});it('创建用户',()=>{cy.get('#createUser').click();// ...});it('删除用户',()=>{// ...});afterEach(()=>{// 每个测试后清理});after(()=>{// 最后执行});});六、与 CI 集成(GitHub Actions 示例)
.github/workflows/cypress.yml:
name:Cypress Testson:[push]jobs:cypress-run:runs-on:ubuntu-lateststeps:-name:Checkoutuses:actions/checkout@v3-name:Cypress runuses:cypress-io/github-action@v5with:browser:chromeheadless:true七、常见坑与解决方案
元素不可见:Cypress 要求元素可见才能交互。确保没有覆盖层或动画。
跨域限制:Cypress 默认不允许访问不同域的页面。解决办法:
将测试和被测应用放在同一域。
使用 cy.origin() 命令(Cypress 10+ 支持跨域测试)。
异步数据不更新:使用 cy.wait() 等待特定网络请求完成,而非固定时间。
无法多标签页:Cypress 不支持多标签页测试。如有此需求,考虑 Playwright。
八、总结与选型建议
Cypress 的优势:
调试体验极佳,时间旅行功能秒杀其他工具。
自动等待机制非常智能,测试代码简洁。
与前端开发工作流无缝集成。
局限性:
仅支持 JavaScript/TypeScript。
不原生支持多标签页、Safari 浏览器。
iframe 支持有限。
选型建议:
如果你是前端开发,测试自己的 React/Vue 应用 → Cypress 首选。
如果你需要跨浏览器(特别是 Safari)或多标签页测试 → Playwright。
如果你有大量遗留 Selenium 代码或需要 IE 测试 → Selenium。