Cypress 入门教程:端到端测试入门
端到端测试可能既缓慢又枯燥。Cypress 改变了我们的测试方式。本教程将教您如何测试应用程序。
Cypress 入门教程:要求
要跟随本教程操作,您的系统需要安装并运行Node.js。此外,对较新版本的 JavaScript 有基本的了解会更有帮助。
Cypress是什么?端到端测试是什么?
端到端测试,或称 UI 测试,是测试 Web 应用程序的众多方法之一。
端到端测试旨在通过测试所谓的用户流程来检查 Web 应用程序是否按预期工作。
端到端测试重要吗?当然重要。但没人喜欢端到端测试。编写端到端测试既耗时又繁琐,而且成本高昂。
另一方面,测试能增强你的信心。你会故意把有缺陷的产品发给用户吗?
隆重介绍Cypress:一个 JavaScript 端到端测试框架。它将让你的工作更轻松。
免责声明
在纯粹主义者对我大喊大叫之前:我知道端到端测试、UI 测试、集成测试等等之间的微妙界限。
亲爱的读者朋友们:测试术语实在太模糊了,我都不知道该怎么解释……。如果您是第一次接触 JavaScript 测试,我建议您阅读《Jest 入门教程:使用 Jest 进行 JavaScript 测试》,其中介绍了单元测试和相关术语。
完成后请返回此处进行端到端测试。
Cypress 新手教程:项目设置
首先创建一个新文件夹,我将其命名为cypress-tutorial,然后进入该文件夹并初始化一个新的 JavaScript 项目:
mkdir cypress-tutorial && cd $_
npm init -y
在此文件夹内创建两个新文件。一个是名为index.html的 HTML 文档:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Cypress tutorial for beginners</title>
</head>
<body>
<main>
<form>
<div>
<label for="name">Name</label>
<input type="name" required name="name" id="name" />
</div>
<div>
<label for="email">Email</label>
<input type="email" required name="email" id="email" />
</div>
<div>
<label for="message">Your message</label>
<textarea id="message" name="message" required></textarea>
</div>
<div>
<button type="submit">SEND</button>
</div>
</form>
</main>
</body>
<script src="form.js"></script>
</html>
这是一个包含多个输入框和一个文本区域的 HTML 表单。接下来,创建一个名为form.js的 JavaScript 文件,其中包含处理表单提交的最简逻辑:
const form = document.forms[0];
form.addEventListener("submit", event => {
event.preventDefault();
});
为了保持简洁,我不会添加任何样式。有了这个简单的项目,我们就可以安装 Cypress 了。
安装 Cypress
要在项目文件夹中安装 Cypress,请运行:
npm i cypress --save-dev
请稍等片刻(需要下载二进制文件),然后运行:
node_modules/.bin/cypress open
Cypress 首次启动后,项目中会出现许多新文件夹。您可以删除示例文件夹,但建议您花点时间浏览一下,因为里面有一些不错的示例。
暂时关闭窗口,前往下一节。
项目启动
要在本地计算机上运行该项目,请确保已安装较新版本的 Node.js,然后运行:
npx serve
这将在http://localhost:5000/启动一个开发服务器。点击该链接即可看到我们的表单:
serve是一个很棒的 NPM 开发包。现在是时候编写我们的第一个测试了!
Cypress 入门教程:编写你的第一个测试
在cypress/integration/form.spec.js中创建一个新文件,并编写你的第一个代码块:
describe("Form test", () => {
//
});
describe是 Cypress 的一个方法(借鉴自 Mocha),用于包含一个或多个相关的测试。每次为某个功能编写新的测试套件时,都将其包装在 describe 代码块中。
如您所见,它需要两个参数:一个用于描述测试套件的字符串和一个用于包装实际测试的回调函数。
接下来我们将遇到另一个名为it 的函数,它实际上是测试块:
describe("Form test", () => {
it("Can fill the form", () => {
//
});
});
如果你已经了解 Jest,你可能记得它可以使用`it`或 ` test`这两个语句块。但 Cypress 并非如此,它只接受 `it` 语句块。
现在是时候进行冒烟测试了!在it 代码块中写入:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
});
});
这里cy是 Cypress 本身,visit是 Cypress 中用于浏览到给定路径的方法。
get方法用于选择页面中的元素。通过这段代码,我们告诉 Cypress“去获取页面中的表单”。
稍后我们将看到 Cypress 的实际应用,但首先,需要进行一些配置!
配置 Cypress
为了简化操作,我们将配置 Cypress。首先,打开package.json 文件,创建一个名为e2e的脚本,指向 Cypress 二进制文件:
"scripts": {
"e2e": "cypress open"
},
接下来打开cypress.json文件并配置基本 URL:
{
"baseUrl": "http://localhost:5000"
}
通过此选项,我们告诉 Cypress 访问我们的开发 URL。(5000 是serve包的默认端口)。
现在,我们准备启动您的第一次测试!
Cypress 新手教程:运行测试
准备好了吗?开发服务器仍然在终端中运行:
npx serve
打开另一个终端并运行:
npm run e2e
你应该会看到Cypress 打开浏览器并浏览页面:
你的第一个测试通过了!visit和get都是Cypress 命令,它们也充当隐式断言,也就是说,如果元素存在于页面中,Cypress 就会认为测试通过。
现在让我们继续扩展测试,看看用户是否可以填写表单:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]').type("Molly");
});
});
这里是另一个 Cypress 命令:`type`,不出所料,它会在我们的第一个文本输入框中输入内容。还要注意用于获取输入元素的 CSS 选择器。
我们不妨再添加一条命令:should。这条命令会创建一个断言,例如,可以用来检查输入是否按预期更新了其状态:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]')
.type("Molly")
.should("have.value", "Molly");
});
});
注意have.value。如果您是第一次接触这个概念,可以点击此处了解更多关于断言的信息。
有了基本的测试,让我们继续下一节。
Cypress 入门教程:更多测试和提交
为了继续进行测试,我们可以检查电子邮件输入:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]')
.type("Molly")
.should("have.value", "Molly");
cy.get('input[name="email"]')
.type("molly@dev.dev")
.should("have.value", "molly@dev.dev");
});
});
我们也可以在文本框中输入:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]')
.type("Molly")
.should("have.value", "Molly");
cy.get('input[name="email"]')
.type("molly@dev.dev")
.should("have.value", "molly@dev.dev");
cy.get("textarea")
.type("Mind you if I ask some silly question?")
.should("have.value", "Mind you if I ask some silly question?");
});
});
如果你保持 Cypress 窗口打开,测试应该会监视你的更改并自动运行:
太好了!锦上添花的是,我们来测试一下提交表单的功能:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]')
.type("Molly")
.should("have.value", "Molly");
cy.get('input[name="email"]')
.type("molly@dev.dev")
.should("have.value", "molly@dev.dev");
cy.get("textarea")
.type("Mind you if I ask some silly question?")
.should("have.value", "Mind you if I ask some silly question?");
cy.get("form").submit();
});
});
测试应该会顺利通过。你会注意到这些命令很简洁明了:type,submit。都是简单的英语。
接下来,让我们在下一节中稍微深入一下,学习XHR 请求测试。
使用 Cypress 模拟 XHR 请求
Cypress 的功能远不止于此,它还能拦截 AJAX 请求。这种方法被称为桩代码(stubbing)。
在开发过程中,桩函数非常方便,您可以选择向 AJAX 请求返回虚假的响应。
为了演示这个功能,让我们在测试中添加一段新的代码:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
// omitted for brevity
cy.server();
cy.route({
url: "/users/**",
method: "POST",
response: { status: "Saved", code: 201 }
});
cy.get("form").submit();
});
});
这里cy.server启动一个“虚拟”服务器,而cy.route用于配置一个虚假的 API 端点。
现在我们再添加一个测试来检查一下:用户提交表单后,我们要测试一下模拟 API 是否有响应。为什么呢?
使用桩函数很有用,因为我们可以在开发过程中完全绕过真正的 API 。让我们用cy.contains扩展测试:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
// omitted for brevity
cy.server();
cy.route({
url: "/users/**",
method: "POST",
response: { status: "Form saved!", code: 201 }
});
cy.get("form").submit();
cy.contains("Form saved!");
});
});
该测试预计会失败,因为没有实现将表单发送到 API 的逻辑。下一节我们将使该测试通过。
将表单数据发送到 API
请耐心阅读本节内容!撰写本文时,Cypress 尚不支持拦截 Fetch 请求。我们将改用XMLHttpRequest。(点击此处了解简介)。
打开form.js 文件并实现以下逻辑:
const form = document.forms[0];
form.addEventListener("submit", event => {
event.preventDefault();
new FormData(form);
});
document.addEventListener("formdata", event => {
const body = Object.fromEntries(event.formData.entries());
const jsonBody = JSON.stringify(body);
const request = new XMLHttpRequest();
request.open("POST", "https://jsonplaceholder.typicode.com/users/");
request.send(jsonBody);
});
在这段代码中,我使用了formdata 事件。当我们调用new FormData时,该事件会被触发。
在事件监听器中,我们使用fromEntries (ECMAScript 2019) 构建一个对象。然后我们将数据发送到 API。
为了使测试通过,我们还需要从 API 获取响应并将其保存到文档中。为此,我们可以监听 XMLHttpRequest 的 onload 事件:
// omit
document.addEventListener("formdata", event => {
const body = Object.fromEntries(event.formData.entries());
const jsonBody = JSON.stringify(body);
const request = new XMLHttpRequest();
request.open("POST", "https://jsonplaceholder.typicode.com/users/");
request.send(jsonBody);
// get the response
request.onload = function() {
const jsonResponse = JSON.parse(this.response);
};
});
最后,为了简单起见,我们可以冒险地(但请千万不要在正式的代码库中这样做):
// omit
request.onload = function() {
const jsonResponse = JSON.parse(this.response);
document.body.innerHTML += `Response from the server: ${jsonResponse.status}`;
};
现在是时候看看考试结果了!
使用 Cypress 对 XHR 请求进行桩化:测试通过
总结一下,以下是cypress/integration/form.spec.js中的完整测试:
describe("Form test", () => {
it("Can fill the form", () => {
cy.visit("/");
cy.get("form");
cy.get('input[name="name"]')
.type("Molly")
.should("have.value", "Molly");
cy.get('input[name="email"]')
.type("molly@dev.dev")
.should("have.value", "molly@dev.dev");
cy.get("textarea")
.type("Mind you if I ask some silly question?")
.should("have.value", "Mind you if I ask some silly question?");
cy.server();
cy.route({
url: "/users/**",
method: "POST",
response: { status: "Form saved!", code: 201 }
});
cy.get("form").submit();
cy.contains("Form saved!");
});
});
以下是form.js的完整代码:
const form = document.forms[0];
form.addEventListener("submit", event => {
event.preventDefault();
new FormData(form);
});
document.addEventListener("formdata", event => {
const body = Object.fromEntries(event.formData.entries());
const jsonBody = JSON.stringify(body);
const request = new XMLHttpRequest();
request.open("POST", "https://jsonplaceholder.typicode.com/users/");
request.send(jsonBody);
// get the response
request.onload = function() {
const jsonResponse = JSON.parse(this.response);
document.body.innerHTML += `Response from the server: ${jsonResponse.status}`;
};
});
需要注意的是,真实的 API 返回的结果格式可能与我们模拟的桩函数不同。开发实际应用时,你需要根据实际系统调整测试用例。
但目前一切正常,如果你一直保持 Cypress 窗口打开,你应该已经看到测试通过了:
您可以在左上角看到路由部分,并在测试输出中看到XHR 存根,这表明 Cypress 已拦截 POST 请求。
这是 Cypress 最棒的功能之一,更不用说它还提供了数十个现成的命令和断言。
通过添加存根,我们可以结束本教程了。干得好!
Cypress 入门教程:结论
希望你通过这篇教程学到了一些新知识,并能将这些概念应用到你的下一个项目中!测试非常重要!
端到端测试不应该那么难:Cypress 让它变得轻松愉快。Cypress 团队真的做得非常出色。
此外,文档简直是无价之宝:Cypress Docs充满了最佳实践和示例。
感谢阅读!
原文发表于我的博客
资源
Stefano Magni 在他的UI 测试最佳实践中提供了许多很好的测试技巧。
文章来源:https://dev.to/valentinogagliardi/cypress-tutorial-for-beginners-getting-started-with-end-to-end-testing-5e0




