E2E 测试最让人头疼的不是写用例,而是用例写完之后没人看得懂、失败了没人查得出原因。Cypress 虽然提供了实时 reload 和交互式界面,但如果不在项目里做工程化约束,测试代码很快就会变成一堆复制粘贴的脚本。这篇文章围绕 Vue 3 项目,讲讲如何把 Cypress 从一个测试工具变成一套可维护的工程化体系,并通过截图、录屏和报告把测试过程真正可视化。

一、在 Vue 3 项目中集成 Cypress
集成的方式很简单,在项目根目录安装 Cypress 并初始化即可。推荐用 npm 安装,这样后续在 CI 环境中可以复用同一套依赖:
npm install cypress --save-dev npx cypress open
执行 npx cypress open 后会自动生成 cypress 目录,包含 e2e、fixtures、support 等子目录。对于 Vue 3 项目,建议在这个基础上再做一层结构调整:把 e2e 目录按业务模块拆分,比如 e2e/login、e2e/order,而不是把所有用例堆在一个目录里。当用例数量超过一百个时,按模块组织的好处会非常明显。
其次是 TypeScript 的支持。Cypress 从 10.x 版本开始原生支持 TS,只需要在 tsconfig.json 中做简单配置,就能让用例文件获得完整的类型提示,包括链式调用的自动补全。这对 Vue 3 项目尤其重要,因为 Vue 3 本身就是 TS 友好的框架,测试代码保持同一套类型体系,团队协作成本会低很多。
另外建议在 package.json 中定义好脚本命令,把打开测试界面、无头模式执行、CI 执行分开管理,避免每次都敲一长串参数。
{
"scripts": {
"cypress:open": "cypress open",
"cypress:run": "cypress run --headed",
"cypress:ci": "cypress run --record --key your-key"
}
}二、自定义命令与 Page Object 模式封装
工程化的核心是复用。原生 Cypress 命令操作的是 DOM,而 Vue 项目中很多交互逻辑是组件级别的,比如登录流程、表单提交、路由跳转。直接在每个用例里写一堆 cy.get 会导致用例和页面结构强耦合,页面稍微改一下,几十个用例全部报错。
解决办法是自定义命令。在 cypress/support/commands.ts 中注册一个 cy.login 命令,把登录逻辑收敛到一处:
Cypress.Commands.add('login', (username: string, password: string) => {
cy.session([username, password], () => {
cy.visit('/login')
cy.get('[data-cy=username]').type(username)
cy.get('[data-cy=password]').type(password)
cy.get('[data-cy=submit]').click()
cy.url().should('include', '/dashboard')
})
})这里有两个细节值得注意。一是 cy.session,它是 Cypress 9.6 之后提供的会话缓存能力,登录一次后后续用例可以复用会话,整体执行时间能缩短一半以上。二是定位策略,推荐统一使用 data-cy 属性而不是 class 或文本内容,因为 class 在 Vue 组件库里经常是动态生成的,文本则会随国际化变化,只有自定义属性最稳定。
对于复杂页面,可以再套一层 Page Object。每个页面封装成一个类,暴露出操作方法和断言方法,用例文件只调用方法,不直接接触 DOM。这样即使页面重构,只需要改 Page Object 一处,用例文件完全不用动。这和写 Vue 组件时的封装思路是一致的:把变化隔离在最小范围内。
三、测试可视化:截图、录屏与报告
可视化是 Cypress 相对其他 E2E 工具的最大优势。默认情况下,用例失败时 Cypress 会自动截图并保存到 cypress/screenshots 目录,录屏文件保存在 cypress/videos 目录。但要真正发挥这些能力的价值,还需要配合配置和报告工具。
首先在 cypress.config.ts 中开启相关配置:
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:5173',
screenshotOnRunFailure: true,
video: true,
videoCompression: 32,
retries: { runMode: 2, openMode: 0 },
reporter: ['mochawesome', {'reporterOptions': {'reportDir': 'cypress/report'}}
],
setupNodeEvents(on, config) {
return config
}
}
})baseUrl 指向 Vue 3 的开发服务器,Vite 项目默认是 5173 端口。retries 配置重试次数,E2E 测试天然存在不稳定因素(网络、动画、异步渲染),适当重试能过滤掉假失败。报告方面推荐 mochawesome,它会生成带截图内嵌的 HTML 报告,失败用例的截图直接显示在报告里,排查时不需要来回切换目录。
除了被动截图,还可以在关键步骤主动截图。比如表单提交后、路由跳转后,用 cy.screenshot('order-created') 记录页面状态。这些截图在评审测试覆盖情况时非常有用,产品同学不看代码也能确认用例是否覆盖了正确的页面。
如果团队规模较大,可以考虑接入 Cypress Cloud,它提供每次执行的完整录屏回放、用例执行时间分析、失败原因聚合等能力。对于本地开发,Cypress 的交互式界面本身就足够可视化:左侧是用例树,右侧实时渲染页面操作过程,配合时间旅行功能可以回看每一步执行时的 DOM 快照,这是传统 Selenium 体系完全做不到的体验。
四、CI 环境与本地开发的工作流整合
E2E 测试只有在 CI 中自动执行才有意义。在 CI 里需要先启动 Vue 应用,再执行 Cypress。常见做法是用 start-server-and-test 这个包把两个步骤串联起来:
npm install start-server-and-test --save-dev # package.json 中 "e2e:ci": "start-server-and-test 'npm run build && npm run preview' http://localhost:4173 'cypress run'"
CI 中要注意两点。一是建议对生产构建做测试而不是开发服务器,因为开发模式下的 HMR 和按需编译会带来不可控的时序问题。二是录屏文件比较大,可以只在失败时上传,通过 CI 的产物机制把 cypress/screenshots 目录作为 artifact 保存,失败时直接下载查看现场。
最后是和单元测试的边界划分。Vue 组件的逻辑用 Vitest 配合 Vue Test Utils 做,E2E 只覆盖核心业务流程,比如登录下单支付这条主链路。把两者混在一起既慢又难维护。合理的比例大概是核心流程的 E2E 用例控制在几十个以内,其余全部下沉到组件测试,这样整个测试体系的执行速度和稳定性都能保持在一个健康的水平。
总结一下,Cypress 在 Vue 3 项目中的工程化落地,关键在于三点:结构化的目录与类型支持、命令与 Page Object 的封装复用、截图录屏报告组成的可视化体系。把这些基建做好之后,写 E2E 用例的效率和维护体验会有质的提升。