01 为什么是Cypress?前端测试的3个致命痛点
传统前端测试工具(Selenium、Puppeteer)有一个共同问题:它们运行在浏览器之外。这就导致3个致命痛点:
痛点1:时间黑洞
Selenium测试一个登录流程,平均需要45秒。因为每个操作都要发送HTTP请求,等待响应,再执行下一步。Cypress直接在浏览器内执行,同样的测试只需8秒。
速度不是优势,是生存法则。
痛点2:调试噩梦
Selenium报错“Element not found”,你永远不知道是元素没加载、网络延迟、还是选择器写错。Cypress提供时间旅行——每一步操作都有截图和录像,你可以回放测试过程,看到每一毫秒发生了什么。
痛点3:异步地狱
前端应用99%的问题出在异步操作。Selenium需要手动添加WebDriverWait,一个等待条件写20行代码。Cypress自动等待——元素可见、动画结束、网络请求完成,全自动处理。
看一段对比代码:
javascript
// Selenium等待方式
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("submit-btn")));
driver.findElement(By.id("submit-btn")).click();
// Cypress等待方式
cy.get('#submit-btn').click(); // 自动等待元素可见、可点击
Cypress的代码量减少80%,可读性提升100%。
02 30分钟搭建你的第一个Cypress测试
环境要求:Node.js 12+,一个前端项目(Vue/React/Angular都行)
步骤1:安装Cypress
bash
# 在你的项目目录下
npm install cypress --save-dev
安装完成后,打开Cypress:
第一次运行会创建默认文件夹结构:
- ►
cypress/e2e:测试文件
- ►
cypress/fixtures:测试数据
- ►
cypress/support:自定义命令
步骤2:编写第一个E2E测试
在cypress/e2e下创建login.spec.js:
javascript
describe('登录功能测试', () => {
beforeEach(() => {
// 每次测试前访问登录页面
cy.visit('http://localhost:3000/login')
})
it('输入正确账号密码,登录成功', () => {
// 输入用户名
cy.get('#username').type('admin')
// 输入密码
cy.get('#password').type('123456')
// 点击登录按钮
cy.get('button[type="submit"]').click()
// 验证跳转到首页
cy.url().should('include', '/dashboard')
// 验证显示欢迎信息
cy.contains('欢迎回来,admin').should('be.visible')
})
it('输入错误密码,显示错误提示', () => {
cy.get('#username').type('admin')
cy.get('#password').type('wrongpassword')
cy.get('button[type="submit"]').click()
// 验证错误提示
cy.get('.error-message').should('be.visible')
.and('contain', '密码错误')
})
})
步骤3:运行测试
命令行运行(无头模式):
浏览器运行(可视化模式):
点击测试文件,Cypress会自动启动浏览器,执行测试,每一步都有实时反馈。
测试不是找bug,是证明系统按预期运行。
03 Cypress核心API:5个命令掌握80%场景
Cypress的命令链式调用,每个命令返回一个Chainable对象。掌握这5个命令,就能覆盖大部分测试场景:
1. cy.get() - 元素选择器
javascript
// CSS选择器
cy.get('.class-name') // 类名
cy.get('#id-name') // ID
cy.get('button') // 标签
cy.get('[data-cy="submit"]') // 属性选择器(推荐)
// 最佳实践:使用data-*属性
// HTML: <button data-cy="submit">提交</button>
cy.get('[data-cy="submit"]').click()
2. cy.contains() - 文本内容定位
javascript
// 查找包含特定文本的元素
cy.contains('登录') // 第一个包含"登录"的元素
cy.contains('button', '提交') // 按钮中的"提交"
3. cy.intercept() - 网络请求拦截(核心功能)
javascript
// 拦截API请求,模拟响应
cy.intercept('GET', '/api/users', {
statusCode: 200,
body: { users: [{ name: '张三' }] }
}).as('getUsers')
// 等待请求完成
cy.wait('@getUsers')
4. cy.wait() - 等待(大多数时候不需要)
javascript
// 显式等待(仅用于特殊场景)
cy.wait(1000) // 等待1秒(不推荐)
// 更好的方式:等待元素出现
cy.get('.loading-spinner', { timeout: 10000 })
.should('not.exist')
5. cy.request() - 直接发送请求
javascript
// 跳过UI,直接测试API
cy.request('POST', '/api/login', {
username: 'admin',
password: '123456'
}).then((response) => {
expect(response.status).to.eq(200)
expect(response.body.token).to.exist
})
实战案例:测试一个搜索功能
javascript
describe('搜索功能测试', () => {
it('输入关键词,显示搜索结果', () => {
// 1. 拦截搜索API
cy.intercept('GET', '/api/search?q=*', {
fixture: 'search-results.json' // 使用测试数据
}).as('searchRequest')
// 2. 访问页面
cy.visit('/search')
// 3. 输入搜索词
cy.get('#search-input').type('Cypress')
// 4. 点击搜索按钮
cy.get('#search-btn').click()
// 5. 等待API请求完成
cy.wait('@searchRequest')
// 6. 验证结果显示
cy.get('.result-item').should('have.length', 3)
cy.get('.result-item').first().should('contain', 'Cypress入门')
})
})
> 测试代码不该比业务代码复杂。
04 从手动到自动:一个表单测试的蜕变
手动测试一个注册表单需要:输入10个字段、选择下拉框、上传文件、点击提交、等待3秒、验证结果。重复10次浏览器版本测试,耗时30分钟。
Cypress测试这段逻辑,耗时3秒。
完整表单测试代码:
javascript
describe('用户注册表单测试', () => {
beforeEach(() => {
cy.visit('/register')
})
it('填写完整信息,注册成功', () => {
// 1. 填写基本信息
cy.get('#username').type('testuser')
cy.get('#email').type('test@example.com')
cy.get('#password').type('Password123!')
cy.get('#confirm-password').type('Password123!')
// 2. 选择下拉框
cy.get('#country').select('中国')
cy.get('#city').select('北京')
// 3. 勾选复选框
cy.get('#agree-terms').check()
// 4. 上传文件
cy.get('#avatar').attachFile('avatar.jpg') // 需要cypress-file-upload插件
// 5. 提交表单
cy.get('button[type="submit"]').click()
// 6. 验证成功提示
cy.contains('注册成功').should('be.visible')
cy.url().should('include', '/welcome')
})
it('密码不匹配,显示错误', () => {
cy.get('#username').type('testuser')
cy.get('#email').type('test@example.com')
cy.get('#password').type('Password123!')
cy.get('#confirm-password').type('DifferentPassword')
cy.get('button[type="submit"]').click()
// 验证密码不匹配错误
cy.contains('密码不一致').should('be.visible')
cy.url().should('not.include', '/welcome')
})
it('必填字段为空,表单阻止提交', () => {
cy.get('button[type="submit"]').click()
// HTML5验证阻止提交
cy.url().should('include', '/register')
// 验证错误提示
cy.get('#username:invalid').should('exist')
cy.get('#email:invalid').should('exist')
})
})
测试数据管理:
在cypress/fixtures/user.json中存储测试数据:
json
{
"validUser": {
"username": "testuser",
"email": "test@example.com",
"password": "Password123!"
},
"invalidUser": {
"username": "",
"email": "invalid-email",
"password": "123"
}
}
测试中使用fixture:
javascript
cy.fixture('user').then((user) => {
cy.get('#username').type(user.validUser.username)
})
自动化测试不是减少工作量,是转移工作量——从重复劳动到智能验证。
05 持续集成:让Cypress在CI/CD中自动运行
测试写好了,但只在本地跑等于没写。真正的价值在于每次代码提交都自动运行。
GitHub Actions配置示例:
在项目根目录创建.github/workflows/test.yml:
yaml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Start application
run: npm start & # 启动开发服务器
- name: Run Cypress tests
uses: cypress-io/github-action@v5
with:
record: true
parallel: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
关键配置说明:
- ►
npm start &:后台启动应用服务器
- ►
record: true:将测试结果上传到Cypress Dashboard
- ►
parallel: true:并行运行测试,减少时间
性能优化技巧:
1. 并行运行测试:将测试拆分为多个文件,并行执行
2. 使用cypress run --spec:只运行特定测试文件
3. 缓存依赖:在CI中缓存node_modules
bash
# 只运行登录测试
npx cypress run --spec "cypress/e2e/login.spec.js"
# 运行多个文件
npx cypress run --spec "cypress/e2e/login.spec.js,cypress/e2e/register.spec.js"
测试报告集成:
安装Mocha报告器:
bash
npm install --save-dev mochawesome
配置cypress.config.js:
javascript
const { defineConfig } = require('cypress')
module.exports = defineConfig({
reporter: 'mochawesome',
reporterOptions: {
reportDir: 'cypress/reports',
overwrite: false,
html: true,
json: true
},
e2e: {
baseUrl: 'http://localhost:3000'
}
})
最佳实践清单:
1. 每个测试独立:不依赖其他测试的状态
2. 使用beforeEach:每个测试前重置状态
3. 避免cy.wait(数字):用cy.get().should()替代
4. 使用自定义命令:封装重复操作
javascript
// 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.login('admin', '123456')
---
测试不是为了证明代码没问题,而是为了证明代码按预期运行。Cypress把这句话变成了3秒的现实。
下一步行动:
1. 在你的项目中运行npx cypress open
2. 写第一个cy.get().click().should()测试
3. 在CI中配置自动运行
前端测试不再是负担。Cypress让每个开发者都能成为测试工程师。
想系统学习软件测试?
千锋教育软件测试课程,零基础入门,企业级项目实战,高薪就业,一站式搞定!
了解课程详情