当前位置: 首页 > news >正文

React端到端测试

下面,我们来系统的梳理关于 React 端到端测试:Cypress 的基本知识点:


一、Cypress 概述

1.1 什么是端到端测试?

端到端测试(E2E)模拟真实用户操作,验证整个应用从用户界面到后端系统的完整工作流程。它关注:

  • 用户界面交互
  • 业务流程验证
  • 系统集成点
  • 真实网络请求和响应

1.2 Cypress 的核心优势

特性描述优势
实时重载测试代码更改后自动重新运行快速迭代开发
时间旅行查看测试每一步的应用程序状态高效调试
自动等待智能等待元素出现和网络请求减少人为等待
网络控制拦截和模拟网络请求测试边缘情况
视频录制自动录制测试过程问题复现和分析
一致性在CI和本地环境运行相同测试减少环境差异问题

1.3 Cypress 架构

Cypress 测试运行器
Node.js 服务器
浏览器
被测应用
网络代理
后端API

二、环境搭建与配置

2.1 安装

npm install cypress --save-dev

2.2 项目结构

cypress/
├── fixtures/       # 测试数据
├── integration/    # 测试用例
├── plugins/        # 插件配置
├── screenshots/    # 失败截图
├── support/        # 自定义命令和全局配置
└── videos/         # 测试录像

2.3 配置文件 (cypress.json)

{"baseUrl": "http://localhost:3000","viewportWidth": 1280,"viewportHeight": 720,"video": true,"retries": {"runMode": 2,"openMode": 0},"env": {"apiUrl": "https://api.example.com","user": {"email": "test@example.com","password": "securePassword123"}}
}

2.4 启动 Cypress

# 交互模式
npx cypress open# CLI 模式
npx cypress run

三、编写测试

3.1 基本测试结构

// cypress/integration/login.spec.js
describe('登录功能测试', () => {beforeEach(() => {// 每个测试前执行cy.visit('/login');});it('使用有效凭证成功登录', () => {cy.get('#email').type(Cypress.env('user').email);cy.get('#password').type(Cypress.env('user').password);cy.get('form').submit();cy.url().should('include', '/dashboard');cy.get('.welcome-message').should('contain', '欢迎回来');});it('使用无效凭证登录失败', () => {cy.get('#email').type('invalid@example.com');cy.get('#password').type('wrongPassword');cy.get('form').submit();cy.get('.error-message').should('be.visible').and('contain', '邮箱或密码错误');});
});

四、Cypress 核心概念

4.1 命令链

Cypress 命令返回链式对象,可连续调用:

cy.get('.todo-list').find('li').first().should('have.class', 'completed').children('.toggle').should('be.checked');

4.2 断言

断言类型示例描述
隐式断言cy.get('button').should('be.enabled')自动重试直到断言通过
显式断言expect(user).to.have.property('name', 'John')传统断言方式
BDD 断言cy.wrap(42).should('equal', 42)行为驱动开发风格

4.3 选择元素

方法示例推荐指数
data-cycy.get('[data-cy="submit-btn"]')⭐⭐⭐⭐⭐
CSS 类cy.get('.btn-primary')⭐⭐⭐⭐
IDcy.get('#login-form')⭐⭐⭐⭐
文本内容cy.contains('登录')⭐⭐⭐
属性cy.get('input[type="email"]')⭐⭐⭐

4.4 常用命令

// 导航
cy.visit('/products');
cy.go('back');
cy.reload();// 交互
cy.get('button').click();
cy.get('input').type('Hello{enter}');
cy.get('select').select('option2');// 表单
cy.get('form').submit();
cy.get('input').clear();
cy.get('input').check();
cy.get('input').uncheck();// 窗口
cy.viewport(1024, 768);
cy.scrollTo('bottom');

五、测试技术

5.1 网络请求控制

// 拦截并模拟响应
cy.intercept('GET', '/api/users', {statusCode: 200,body: [{ id: 1, name: 'Alice' },{ id: 2, name: 'Bob' }]
}).as('getUsers');// 触发请求
cy.visit('/users');// 等待请求完成
cy.wait('@getUsers').then((interception) => {expect(interception.response.statusCode).to.eq(200);
});// 修改真实响应
cy.intercept('POST', '/api/login', (req) => {req.reply((res) => {res.body.token = 'mock_token_123';return res;});
});

5.2 数据库操作

// 使用 cy.task 与 Node.js 交互
// cypress/plugins/index.js
module.exports = (on, config) => {on('task', {resetDatabase: () => {// 调用数据库重置脚本return exec('npm run db:reset');},createUser: (userData) => {// 数据库创建用户return db('users').insert(userData);}});
};// 在测试中使用
beforeEach(() => {cy.task('resetDatabase');
});it('创建新用户', () => {cy.task('createUser', { name: 'Test User', email: 'test@example.com' });cy.visit('/users');cy.contains('Test User').should('be.visible');
});

5.3 文件操作

// 上传文件
cy.get('input[type="file"]').attachFile('avatar.jpg');// 下载文件
cy.intercept('GET', '/downloads/report.pdf', {fixture: 'report.pdf'
}).as('downloadReport');cy.get('#download-btn').click();
cy.wait('@downloadReport');// 验证文件内容
cy.readFile('cypress/downloads/report.pdf').should('exist');

5.4 认证处理

// 自定义登录命令
// cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {cy.session([email, password], () => {cy.visit('/login');cy.get('#email').type(email);cy.get('#password').type(password);cy.get('form').submit();cy.url().should('include', '/dashboard');});
});// 在测试中使用
beforeEach(() => {cy.login(Cypress.env('user').email, Cypress.env('user').password);
});

六、测试组织与实践

6.1 测试组织结构

// 页面对象模式 (Page Object)
// cypress/support/pages/LoginPage.js
class LoginPage {visit() {cy.visit('/login');}fillEmail(email) {cy.get('#email').type(email);}fillPassword(password) {cy.get('#password').type(password);}submit() {cy.get('form').submit();}assertErrorMessage(message) {cy.get('.error-message').should('contain', message);}
}export default new LoginPage();// 在测试中使用
import LoginPage from '../support/pages/LoginPage';describe('登录测试', () => {it('显示错误消息', () => {LoginPage.visit();LoginPage.fillEmail('invalid@example.com');LoginPage.fillPassword('wrong');LoginPage.submit();LoginPage.assertErrorMessage('邮箱或密码错误');});
});

6.2 测试数据管理

// 使用 fixtures
// cypress/fixtures/users.json
{"admin": {"email": "admin@example.com","password": "adminPass123","role": "admin"},"customer": {"email": "customer@example.com","password": "customerPass123","role": "customer"}
}// 在测试中使用
beforeEach(() => {cy.fixture('users').as('users');
});it('管理员登录', function() {const admin = this.users.admin;cy.login(admin.email, admin.password);cy.get('.admin-dashboard').should('be.visible');
});

6.3 实践

  1. 选择器策略

    • 使用 data-cy 属性作为首选选择器
    • 避免使用易变的 CSS 类名
    • 优先使用文本内容而非位置选择器
  2. 测试设计

    • 每个测试只验证一个功能点
    • 使用描述性的测试名称
    • 避免测试依赖(每个测试独立运行)
  3. 性能优化

    • 使用 cy.session() 缓存登录状态
    • 减少不必要的页面加载
    • 并行运行测试

七、持续集成(CI)

7.1 GitHub Actions 配置

name: E2E Testson: [push, pull_request]jobs:cypress-run:runs-on: ubuntu-lateststeps:- name: Checkoutuses: actions/checkout@v2- name: Install dependenciesrun: npm ci- name: Start serverrun: npm start &- name: Run Cypress testsuses: cypress-io/github-action@v4with:start: npm startwait-on: 'http://localhost:3000'browser: chromerecord: trueenv:CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

7.2 并行运行测试

npx cypress run --record --key <record-key> --parallel

八、调试技巧

8.1 交互式调试

it('调试示例', () => {cy.visit('/');// 暂停测试cy.debug();// 或使用 .then 调试cy.get('button').then(($btn) => {debugger; // 在浏览器开发者工具中调试$btn.click();});
});

8.2 时间旅行

Cypress 测试运行器显示每个命令的快照:

  • 点击命令查看应用状态
  • 悬停在命令上查看 DOM 状态
  • 使用 “before” 和 “after” 查看变化

8.3 控制台输出

cy.get('table').then(($table) => {console.log('Table content:', $table.text());// Cypress 日志Cypress.log({name: 'table',message: $table.text()});
});

九、常见问题解决方案

9.1 元素不可见错误

问题CypressError: Timed out retrying: expected '<button>' to be 'visible'

解决方案

// 确保元素在视图中
cy.get('button').scrollIntoView().should('be.visible');// 检查是否被覆盖
cy.get('button').click({ force: true });// 等待元素变为可见
cy.get('.loading-spinner', { timeout: 10000 }).should('not.exist');
cy.get('button').should('be.visible');

9.2 跨域问题

问题CypressError: cy.visit() failed because you are attempting to visit a second unique domain

解决方案

// 禁用 Web 安全(谨慎使用)
{"chromeWebSecurity": false
}// 或使用代理处理跨域请求
cy.intercept('https://api.example.com/*', (req) => {req.headers['origin'] = 'http://localhost:3000';
});

9.3 测试稳定性

问题:测试有时通过,有时失败

解决方案

// 增加重试次数
{"retries": {"runMode": 2,"openMode": 0}
}// 使用自定义重试逻辑
Cypress.Commands.overwrite('should', (originalFn, subject, expectation, ...args) => {const options = {interval: 500,timeout: 10000};return originalFn(subject, expectation, ...args, options);
});

十、Cypress 生态系统

10.1 常用插件

插件用途安装
cypress-real-events真实浏览器事件npm install cypress-real-events
cypress-axe无障碍测试npm install cypress-axe
cypress-image-snapshot视觉回归测试npm install cypress-image-snapshot
cypress-grep测试过滤npm install cypress-grep
cypress-fail-fast快速失败npm install cypress-fail-fast

10.2 报告生成

# 安装 Mochawesome 报告器
npm install mochawesome mochawesome-merge mochawesome-report-generator --save-dev
// cypress.json
{"reporter": "mochawesome","reporterOptions": {"reportDir": "cypress/reports","overwrite": false,"html": false,"json": true}
}

十一、总结

Cypress 测试金字塔

端到端测试
关键用户流程
核心业务场景
跨系统集成
登录/注册
下单流程
支付处理
数据同步
第三方服务
API集成

总结

  1. 测试策略

    • 优先覆盖关键用户流程
    • 避免过度测试实现细节
    • 结合单元和集成测试
  2. 技术选择

    • 使用页面对象模式提高可维护性
    • 利用网络拦截测试边缘情况
    • 实现自定义命令复用逻辑
  3. 持续改进

    • 定期审查测试覆盖率
    • 修复不稳定的测试
    • 集成到CI/CD流水线
  4. 团队协作

    • 开发与测试共同编写测试
    • 使用相同测试语言(Gherkin可选)
    • 共享测试报告和视频
http://www.lryc.cn/news/623901.html

相关文章:

  • 通达信【牛股妖股埋伏】副图+选股指标
  • Shell脚本-while循环应用案例
  • nn.Module模块介绍
  • 计算机视觉(一):nvidia与cuda介绍
  • OpenMemory MCP发布!AI记忆本地共享,Claude、Cursor一键同步效率翻倍!
  • 【Linux】文件基础IO
  • Agent开发进阶路线:从基础响应到自主决策的架构演进
  • Python使用数据类dataclasses管理数据对象
  • 【C2000】C2000例程使用介绍
  • Python进行中文分词
  • MySQL定时任务详解 - Event Scheduler 事件调度器从基础到实战
  • Blender模拟结构光3D Scanner(二)投影仪内参数匹配
  • 火狐(Mozilla Firefox)浏览器离线安装包下载
  • 学习Python中Selenium模块的基本用法(5:程序基本步骤)
  • Python数据类型转换详解:从基础到实践
  • Python 基础语法(二)
  • 0️⃣基础 认识Python操作文件夹(初学者)
  • Linux:TCP协议
  • RK3568平台开发系列讲解:PCIE trainning失败怎么办
  • 深入解析函数指针及其数组、typedef关键字应用技巧
  • 0-12岁幼儿启蒙与教育
  • CF2121C Those Who Are With Us
  • 2001-2024年中国玉米种植分布数据集
  • 【牛客刷题】01字符串按递增长度截取并转换为十进制数值
  • Day07 缓存商品 购物车
  • 14.web api 5
  • LEA(Load Effective Address)指令
  • 19.5 「4步压缩大模型:GPTQ量化实战让OPT-1.3B显存直降75%」
  • 混沌工程(Chaos engineering):系统韧性保障之道
  • 图解希尔排序C语言实现