Cypress 使用指南

什么是 Cypress

Cypress 是一个现代化的前端端到端(End-to-End,E2E)测试框架。它被设计用于对 Web 应用程序进行自动化测试,可以模拟用户在真实环境中与应用程序的交互行为。

以下是 Cypress 的一些关键特点和优势:

  1. 简单易用: Cypress 具有简洁而直观的 API,使得编写和维护测试用例非常容易。
  2. 强大的调试能力: Cypress 提供了丰富的调试工具,开发人员可以通过断点、控制台日志和实时命令行等方式方便地调试测试用例。
  3. 可见即可信: Cypress 在测试过程中会记录并生成视频,可以可视化地查看每个测试用例的执行过程,方便回放和排查问题。

为什么要用 Cypress?

  1. JavaScript 技术栈: Cypress 是以 JavaScript 为技术栈编写的,而我们团队 BCS 端都是使用 JavaScript 技术栈为主,切换为以 JavaScript 为主的自动化测试框架是一件低成本的事情。
  2. 简化测试流程: Cypress 提供了一个全面的测试解决方案,可以覆盖从用户界面到后端服务器的整个应用程序。它集成了测试框架、断言库和模拟工具,使得编写和管理测试用例变得更加简单。
  3. 可靠性和稳定性: Cypress 在内部运行测试代码,与浏览器直接交互,并可以完全控制应用程序的行为。这种直接集成性能更佳,使得测试更加可靠,能够捕获更多的错误和问题。并且随着”全功能模板”的稳定迭代我们需要一种自动化测试工具进行回归测试以减少测试成本。
  4. 易于调试: Cypress 提供了强大的调试工具,包括断点、控制台日志和实时命令行等功能,方便开发人员在测试过程中进行问题排查和调试。
  5. 可视化回放: Cypress 会记录并生成视频,可以可视化地查看每个测试用例的执行过程。这对于团队合作、问题复现和审查都非常有价值。

Cypress 为团队带来的价值

  1. 沟通成本:Cypress 提供了实时可视化测试运行状态的功能,你可以在浏览器中实时查看测试的运行情况、命令的执行顺序和元素的状态。此外,Cypress 还可以自动重放失败的测试命令,以帮助你快速调试和修复问题减少沟通成本。
  2. 与开发者工具的无缝集成:Cypress 与开发者工具(如 DevTools)无缝集成,使你可以直接在 Cypress 界面中查看应用程序的 DOM、网络请求、响应等详细信息。这极大地简化了调试和排查问题的过程。
  3. 自动化测试 : 自动化测试可以大大提高测试过程的效率。相比手动测试,自动化测试可以更快地执行测试用例,并且始终保持一致性
  4. 节省时间和成本:自动化测试可以在软件开发周期中节省大量的时间和成本。自动运行的测试套件可以在短时间内执行大量的测试用例,不仅可以及早发现和修复问题,还可以减少重复劳动和人力资源的需求。

使用 Cypress 需要达到的目标

  1. 减少手动测试工作量:Cypress 提供了自动化测试的能力,可以模拟用户与应用程序进行交互的整个过程。这意味着你可以编写一次测试代码,然后反复运行,而不需要手动重复执行相同的测试步骤。这样可以显著减少手动测试的工作量。

以下为”全功能模板”添加的测试用例运行图。对比之前的人工测试,自动化的测试节省了时间和保证测试的一致性和可重复性。

img

  1. 提高测试的可靠性:Cypress 使用实时重放和监控的方式执行测试,确保测试命令的正确执行顺序和应用程序的正确状态。它还提供了自动等待和重试机制,以处理异步操作和不稳定性。这些功能提高了测试的可靠性,减少了测试失误和漏洞。
  2. 提高团队协作效率:Cypress 提供了易于编写和维护的测试代码,具有直观的 API 和丰富的断言库。这使得团队成员可以更轻松地编写、理解和修改测试代码,促进了团队的协作效率和开发速度。
  3. 加强测试质量和覆盖度 : 通过 Cypress 的提供的测试 API 提升”全功能模板”的测试质量并将覆盖度提升到 90 %以上。

Cypress 的使用

创建一个项目

1
pnpm create vite

image-20230717160123500

安装 Cypress

1
pnpm add cypress -D

在 package.json 中添加如下命令:

1
2
3
4
"scripts": {
"cypress:open": "cypress open",
"cypress:run-ci": "cypress run"
}

随后运行命令启动项目

1
2
pnpm dev;
pnpm cypress:open;

选择E2E

image-20230717155513987

第一次进入 Cypress 会基于我们一些实例代码

image-20230717155559769

选择 Chrome 进行测试

image-20230717160359726

然后我们点击 Scaffold example specs 生成一些示例代码

image-20230717160713367

我们点击对应的文件进入即可执行需要的文件

image-20230717161020353

image-20230717161038174

进入对应的代码我们发现一片爆红那是因为创建的项目默认集成了 eslint,

image-20230717161425009

我们需要安装如下插件

eslint-plugin-cypress

配置完毕如下图

image-20230717161710208

常用的 API 介绍

  1. 获取元素
1
cy.get("HTMLElement") // jQuery 式
  1. 获取包含特定文本的元素
1
cy.contains("文字")
  1. 断言特定条件是否成立
1
cy.get(body).should("exist") // 验证 body 是否存在
  1. 切换路由
1
cy.visit("/home")
  1. 拦截和处理网络请求
1
cy.intercept(route, handler)
  1. 等待一段时间
1
cy.wait(ms)
  1. 输入文本
1
cy.type(text)
  1. 图片上传
1
2
npm i cypress-file-upload -D
cy.get(dom).attachFile(imgPath);

更多 API 请查看官方文档

实践

在阅读了以上的 API 我们来自行编写一个 E2E 测试

要求

1
2
3
4
5
1. 访问 https://preview.pro.ant.design/user/login/
2. 输入账号密码登录 admin ant.design
3. 切换路由到 https://preview.pro.ant.design/form/basic-form
4. 填写表单
5. 验证提交成功

完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const username = "admin";
const passworld = "ant.design";

describe("Antd", () => {
beforeEach(() => {
login();
});

afterEach(() => {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
});

it("应该填写表单完毕", () => {
cy.visit("https://preview.pro.ant.design/form/basic-form/");
cy.get("#basic_title").type("标题");
// 日期框
cy.get("#basic_date").click();
cy.get(".ant-picker-cell-inner").contains("15").click();
cy.get(".ant-picker-cell-inner").contains("16").click();
cy.get("#basic_goal").type("目标描述");
cy.get("#basic_standard").type("衡量标准");
cy.get("#basic_publicType > :nth-child(1) > :nth-child(2)").click();
cy.get(".ant-space > :nth-child(1) > .ant-btn").click();
cy.contains("提交成功").should("exist");
});
});

function login() {
cy.visit("https://preview.pro.ant.design/user/login/");
cy.get("#username").type(username);
cy.get("#password").type(passworld);
cy.get(".ant-btn").click();
cy.contains("登录成功").should("exist");
}

可优化的点,登录功能是每一个 it 都需要的,所以可以注册到全局命令

cypress/support/commands.js

1
2
3
4
5
6
7
8
9
10
11
Cypress.Commands.add("login", () => {
const username = "admin";
const passworld = "ant.design";
cy.visit("https://preview.pro.ant.design/user/login/");
cy.get("#username").type(username);
cy.get("#password").type(passworld);
cy.get(".ant-btn").click();
cy.contains("登录成功").should("exist");
});

// 之后在任意的 cy 文件中直接调用 cy.login() 即可登录

编写规范

  1. 原则:单元测试的目标是测试每一个功能在特定输入下,输出结果和预期结果的一致性。
  2. 单一性:一个单元测试只应该关注一个功能点,单元测试不是用来覆盖复杂复合用例,不能杂糅这样的目标到单元测试中。
  3. 可维护性:尽可能简单,清晰,易懂,任何单元测试都不应该包含复杂的逻辑。
  4. 边界测试:单元测试应该考虑一些不以达到的边界条件。
  5. 测试隔离:除了系列测试之外,应该尽量保持测试例之间的逻辑和测试环境隔离,也就是说测试后有必要的话应该清理测试例对环境造成的污染。
  6. 保持维护:这可能是单元测试中最重要的一条铁律,任何功能的修改和增删都会对单元测试造成影响,应该持续对单元测试进行更新和维护。
  7. 数据: 新增的数据名以 cypress 开头在执行完毕之后需要删除以维护测试环境的数据。

云效流水线集成

流水线环境采用自定义构建的 Docker 镜像文件上传至阿里云镜像站下载使用,当创建 PR 的时候就会执行流水线进行 E2E 测试,只有通过才能合并代码以保证代码的稳定性和健壮性。

常见问题处理办法

  1. 在命令行中运行单个测试用例
1
cypress run -s cypress/e2e/Activity/04-Task.cy.js
  1. 获取不到非可视区的元素

当 Cypress 在 CI/CD 环境中运行时,经常会出现 Cypress 调用 scrollTo() 滚动后又回到原位的情况。这是因为 CI/CD 环境中通常以无头模式运行 Cypress(即无可视化界面),而滚动操作通常依赖于页面的可视区域。

无头模式下的浏览器可能会对滚动操作做出一些优化或默认行为,在滚动之后自动恢复到初始状态。这种行为不符合我们在网页上手动滚动的预期效果。

为了解决这个问题,可以尝试使用 Cypress 的 scrollTo() 方法的 timeout 参数,增加滚动操作的持续时间,以确保滚动操作生效。

例如:

1
cy.scrollTo('bottom', { duration: 2000 });

这样会将滚动操作的持续时间延长到 2 秒钟,让滚动更充分地发生。你可以根据需要调整持续时间。

此外,还可以尝试在滚动操作后添加一些额外的断言,例如检查滚动位置是否正确或检查滚动后的元素是否可见,以确保滚动操作生效并且不会回到原位。

1
cy.scrollTo('bottom').should('have.prop', 'scrollTop', 1000);
  1. 移除 cy.wait(someTime)

在使用Cypress时,使用cy.wait()命令确实存在过度依赖网络环境和测试机器性能的问题。为了更好地控制等待时间,推荐使用cy.intercept()命令与cy.wait()结合使用,以明确指定需要等待的网络资源。

1
2
cy.intercept('https://baidu.com/').as('fetchURL');
cy.wait('@fetchURL');