5月27日 23:00

Cypress 数据驱动测试怎么实现?从 fixture 到实战的完整方案

Cypress 的数据驱动测试能让你用同一套测试逻辑跑多组数据,避免为每种输入单独写用例。比如测试登录,与其写 5 个几乎相同的 it 块分别测试不同账号,不如把账号数据抽到 fixtures 文件,用一个循环搞定。本文从 cy.fixture() 基础用法讲起,覆盖 .each() 遍历、动态数据源、常见踩坑和最佳实践。

用 cy.fixture() 加载测试数据

fixture 文件怎么写

Cypress 的 fixtures 目录默认在 cypress/fixtures/,数据格式用 JSON。创建一个登录用的测试数据文件:

json
// cypress/fixtures/users.json [ { "username": "admin", "password": "admin123", "expectSuccess": true }, { "username": "guest", "password": "wrong", "expectSuccess": false }, { "username": "locked_user", "password": "pass123", "expectSuccess": false } ]

每个数据项里除了输入值,还加了期望结果的字段。这样正负用例都能覆盖,数据本身就表达了测试意图。

在测试中加载 fixture

cy.fixture() 加载 fixtures 目录下的 JSON 文件,返回解析后的数据。最基础的写法:

javascript
describe('登录功能 - 数据驱动', () => { it('用 fixture 数据验证多种账号', () => { cy.fixture('users.json').then((users) => { users.forEach((user) => { cy.visit('/login') cy.get('#username').clear().type(user.username) cy.get('#password').clear().type(user.password) cy.get('button[type="submit"]').click() if (user.expectSuccess) { cy.url().should('include', '/dashboard') } else { cy.get('.error-message').should('be.visible') } }) }) }) })

这里有个实际问题:forEach 在一个 it 块里跑多组数据,如果中间某组失败,Cypress 会直接中断,后面的数据组不会执行。要解决这个问题,得换一种方式。

用 .each() 替代 forEach

为什么 forEach 不够好

forEach 不是 Cypress 命令,它不会进入 Cypress 的命令队列。这意味着:

  • 某组数据断言失败后,剩余数据直接跳过
  • 无法利用 Cypress 的重试机制
  • 调试时很难定位是哪组数据出了问题

用 Cypress .each() 逐条执行

Cypress 的 .each() 是一个命令,每条数据生成独立的命令序列,失败行为更可控:

javascript
describe('登录功能 - 数据驱动', () => { beforeEach(() => { cy.visit('/login') }) it('验证多种账号的登录结果', () => { cy.fixture('users.json').then((users) => { cy.wrap(users).each((user) => { cy.visit('/login') cy.get('#username').clear().type(user.username) cy.get('#password').clear().type(user.password) cy.get('button[type="submit"]').click() if (user.expectSuccess) { cy.url().should('include', '/dashboard') } else { cy.get('.error-message').should('be.visible') } }) }) }) })

cy.wrap(users).each() 把数组包装成 Cypress 对象再遍历,每条数据都在命令队列里排队执行。

更推荐:每个 it 块跑一条数据

如果想让每组数据完全独立(一条失败不影响其他),把数据驱动拆到 it 层面更合适:

javascript
describe('登录功能 - 数据驱动', () => { let users before(() => { cy.fixture('users.json').then((data) => { users = data }) }) users.forEach((user, index) => { it(`账号 ${user.username} 登录测试`, () => { cy.visit('/login') cy.get('#username').type(user.username) cy.get('#password').type(user.password) cy.get('button[type="submit"]').click() if (user.expectSuccess) { cy.url().should('include', '/dashboard') } else { cy.get('.error-message').should('be.visible') } }) }) })

这种方式下,Cypress 报告里每条数据都有独立的测试用例名,失败定位一目了然。需要注意的是 before 里加载 fixture,forEach 在 describe 层面展开 it 块,这是 Cypress 社区推荐的模式。

从 API 动态获取测试数据

不是所有测试数据都适合写死在 fixture 文件里。比如你要测的用户列表经常变动,可以用 cy.request() 从接口拿数据:

javascript
describe('API 数据驱动', () => { it('从接口获取数据并验证', () => { cy.request('GET', '/api/test-users').then((response) => { expect(response.status).to.eq(200) const users = response.body cy.wrap(users).each((user) => { cy.visit('/login') cy.get('#username').type(user.username) cy.get('#password').type(user.password) cy.get('button[type="submit"]').click() cy.get('.welcome').should('contain', user.username) }) }) }) })

几个注意点:

  • 确保 /api/test-users 接口稳定,否则测试会因为数据获取失败而挂掉
  • 数据量大时考虑截取前 N 条,避免测试运行时间过长:const users = response.body.slice(0, 10)
  • 可以在 before 里请求一次数据,后续 it 块复用,减少重复请求

常见踩坑

fixture 文件路径写错

cy.fixture('users')cy.fixture('users.json') 都能工作,Cypress 会自动补全扩展名。但如果你的 fixtures 目录有子目录,路径要写全:cy.fixture('auth/users') 对应 cypress/fixtures/auth/users.json

数据驱动测试跑得慢

每组数据都要重新走一遍页面交互,数据多了自然慢。几个优化方向:

  • 减少不必要的 cy.visit(),如果页面状态可以重置,用 cy.reload() 更快
  • 只保留核心场景数据,边界数据挑有代表性的几条就够了
  • cy.session() 缓存登录状态,避免每次重新走登录流程

forEach 里状态没清理

在循环里跑登录测试,上一条数据的输入残留在页面上,导致下一条数据输入错乱。解决方法是在每轮循环开始时清理字段:

javascript
cy.wrap(users).each((user) => { cy.visit('/login') // 重新访问页面,相当于重置状态 // 或者手动清理: // cy.get('#username').clear() // cy.get('#password').clear() cy.get('#username').type(user.username) cy.get('#password').type(user.password) cy.get('button[type="submit"]').click() })

数据驱动测试的最佳实践

数据与逻辑分离:fixture 文件只放数据,测试脚本只管逻辑。数据文件纳入版本控制,修改数据不影响测试代码。

覆盖正负场景:数据集里同时包含成功和失败的用例。很多团队只测 happy path,失败场景反而更容易出问题。

命名要清晰:fixture 文件名和每个 it 块的描述都要能直接看出测的是什么。账号 locked_user 登录测试第 3 条数据测试 有用得多。

控制数据规模:数据不是越多越好。5 到 10 条覆盖核心场景的数据比 50 条冗余数据更实用,跑起来也更快。

接口数据做好兜底:用 cy.request() 拿数据时,加一个状态码断言确保数据源没问题,别让接口异常拖垮整个测试套件。

数据驱动测试的本质是让测试逻辑写一次、数据跑多遍。Cypress 提供了 cy.fixture().each()cy.request() 这几件工具,组合起来能覆盖大部分场景。从 fixture 文件开始试,遇到动态数据再引入 cy.request(),遇到调试困难就拆成独立 it 块——按这个顺序推进,基本不会踩大坑。

标签:Cypress