Cypress 使用手册

版本号 ^11.2.0

前提准备与说明

安装 cypress 的客户端软件作为运行环境.

在项目根目录下安装 cypress

配置启动脚本

Copy
{
"e2e": "cypress open", // 打开客户端手动选择性执行
"test": "cypress run && cypress run --component" // 在命令行里，执行所有的测试用例
}

Tip

cypress 的 api ，都是以同步的写法，来异步执行的。比如 async await 那样，它不需要显示声明，直接调用。比如在获取元素的时候，它会有相应的等待时间来异步处理。

使用场景

前往某个页面

Copy
cy.visit(url);

获取元素

选择器匹配

Copy
// 这种匹配方式，可以参考 querySelector 的行为
cy.get(selector);

内容匹配

Copy
// 内容匹配，下列语句将会匹配包含了 hello 文本的元素
cy.contain('hello');

事件调用

基本事件

Copy
// 以调用点击事件为例，其它事件也可以同样如此
cy.get(selector).click();

复杂事件

Copy
// 这种方式，除了触发基本事件，还可以触发 mousedown、mouseup 这类的事件，来模拟复杂的用户操作
cy.get(selector).trigger(eventName);

内容校验

Copy
// 该语法是在判断选中的元素，应该有 hello 的内容
cy.get(selector).should('have.value', 'hello');

// 判断元素的 top 值，是否为 10 px;
cy.get(selector).should('have.css', 'top').and('eq', '10px');

结合 ts 的语法提示，should 的第一个参数，还有很多其它的行为来进行校验

模拟按键

Copy
// 按住 ctrl
cy.get('body').type('{ctrl}', { release: false });

// 抬起 ctrl
cy.get('body').type('{ctrl}', { release: true });

接口请求

获取接口的调用结果

Copy
// 开始监听某个接口
cy.intercept({
method: 'GET',
// 比如监听 /api/login?account=xxx，写法为：/api/login*
url: `接口的path部分`,
}).as('test');

// 此处注意。在监听的代码开始执行时，后面的请求行为如果不是该接口，就会报错。也就是说，在开始监听后，需要确保接下来的请求一定是这个监听的请求。所以不能一开始就去监听。推荐做法就是在监听后，确定后续行为马上可以监听到该接口来处理，比如在这里触发某个按钮的点击行为，来调用接口

// 获取接口的请求结果
cy.wait('@test')
.its('response.body')
.then(data => {
cy.log(JSON.stringify(data));
});

根据上述描述，可以封装如下方法使用

Copy
export const watchRequest = <T>(url: string, trigger: () => void, callback: (data: T) => void) => {
cy.intercept({
method: 'GET',
url: `${url}*`,
}).as(url);

trigger();

cy.wait(`@${url}`)
.its('response.body')
.then(data) => {
callback(data);
});
};

是否正常访问某个页面

在上述场景情况下，做如下调整，即可大致确认某个页面是否属于正常访问。

Copy
/**
* 某个页面可以正常访问
* @param url 页面地址
* @param requestCount 判定页面发起了多少个请求都是正常的
*/
export const normalVisit = (url: string, requestCount = 30) => {
cy.intercept({
url: '*',
}).as('apiCheck')

cy.visit(url);

Array(requestCount).fill('').forEach(() => {
cy.wait('@apiCheck').then((interception) => {
expect(Boolean(interception.response)).equal(true);
assert.isNotNull(interception.response!.body);
expect(interception.response!.statusCode).to.be.oneOf([200, 304])
});
});
}

修改请求参数

Copy
// 修改 get 请求的参数
cy.intercept('/api/test*', (req) => {
const request = JSON.parse(req.query as string);
req.query.account = 2;
});

注意：修改的请求参数，在控制台里面看请求时，参数没有发生显示变化，实际上已经改变了。

强制等待

Copy
cy.wait(1000); // 等待1s

自定义指令

元素是否存在

声明类型

Copy
/// <reference types="cypress" />
/// <reference types="cypress-wait-until" />

declare namespace Cypress {
interface Chainable<Subject> {
/**
* 检查元素是否存在
*
* @param selector 选择器
* @return Chainable<Subject>
*/
isElementExist(selector: string): Chainable<Subject>;
}
}

定义指令

Copy
/// <reference types="cypress" />
/// <reference types="cypress-wait-until" />

require("cypress-wait-until");

export function isElementExist(selector: string) {
return cy
.window()
.then(($window) => $window.document.querySelector(selector));
}

Cypress.Commands.add("isElementExist", isElementExist);

使用指令

Copy
cy.isElementExist(".shoe-id").then((value) => {
if (value) {
// ...
} else {
// ...
}
}

测试数据

cypress/fixtures 目录下的 json 文件可以当作测试数据来用，例如: user.json:

Copy
{
"username": "hello world"
}

那么在使用的时候就可以这么获取

Copy
cy.fixture('user').then(res => {
console.log(res.username);
})