端到端测试可能很慢而且很无聊。Cypress改变了我们的测试方式。在此教程中,了解如何使用Cypress测试你的应用程序。
要求
要想继续接下来的学习,你需要在电脑上安装好nodejs,以及对新版本es特性和语法有一定的了解最好。
Cypress是什么?什么是端到端测试?
端到端测试或UI测试是测试Web应用程序的多种方法之一。
端到端测试应该通过测试所谓的用户流来检查Web应用程序是否按预期工作。
端到端测试重要吗?是的。但是没有人喜欢端到端测试。它们编写起来可能很慢,麻烦,成本很高。
但是,测试又使你满怀信心。你是否希望将有bug的产品给到你的用户?
Cypress:一个Javascript端到端测试框架。这将使您的生活更轻松。
免责声明
在纯粹主义者对我大喊之前:我知道端到端测试,UI测试,集成测试等之间的微妙界限。
对于读者你来说,测试技术非常的模糊,我甚至不太好描述。如果你是第一次使用JavaScript测试,我建议阅读初学者Jest教程:JavaScript测试Jest入门,以获取有关单元测试和术语的介绍。
当你阅读完上面的入门文章之后,就可以返回这里来继续看了。
初始化项目
首先创建一个新文件夹, 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表单,其中包含大量input和textarea元素。接下来,在form.js中创建一个具有处理表单提交的最小逻辑的JavaScript文件:
const form = document.forms[0];
form.addEventListener("submit", event => {
event.preventDefault();
});
复制代码
请注意,我不会添加样式来使事情保持简单。通过这个简单的项目,我们准备安装 Cypress。
安装 Cypress
在项目文件夹中,运行一下命令:
npm i cypress --save-dev
复制代码
稍等一下(需要下载二进制文件),然后运行:
node_modules/.bin/cypress open
复制代码
Cypress 将首次启动,并且一堆新文件夹将出现在您的项目中。您可以删除示例文件夹,但请稍候片刻,因为有一些不错的示例。
现在关闭窗口,然后转到下一部分。
开始项目
要在本地计算机上提供项目,请确保安装了较新版本的Node.js,然后运行:
npx serve
复制代码
执行之后,会启动一个服务,在 http://localhost:5000/,打开链接应该可以看到如下表单:
serve 是一个不错的NPM开发包。现在该写我们的第一个测试了!
编写你的第一个测试
在 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。
现在开始冒烟测试。在代码块中这样写:
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)。 现在我们准备开始你的第一个测试!
运行测试
在一个终端中运行以下命令:
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,毫无疑问,这是将内容输入到第一个 input 中。还要注意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。如果您不熟悉此概念,可以在此处了解有关断言的更多信息。
进行了最小限度的测试之后,让我们进入下一节。
更多的测试和提交表单
要继续我们的测试,我们可以检查电子邮件输入:
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 保持打开,则测试应观察你的更改并自动运行:
舒服!趁热打铁,让我们通过 sumbit 来测试表单的提交。
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请求返回假响应。
为了演示此功能,让我们在测试中添加一段新代码:
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扩展测试:
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的完整代码
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存根,这表明赛普拉斯已拦截POST请求。
这是 Cypress 的最佳功能之一,不包括数十个可供使用的命令和断言。
通过存根,我们可以结束本教程。优秀!
结论
我希望您从本教程中学到了新知识,并将这些概念应用于下一个项目!测试很重要!
端到端测试应该不难:Cypress 使它非常简单且令人愉悦。Cypress 的团队确实做到了。
另外,文档是最直接的:Cypress Docs 包含了最佳实践和示例。
谢谢阅读!