Cypress是基于js的前端自动化测试工具,引入cypress框架可以减少测试人员重复手工测试的工作量,也可以方便开发人员自测,便于系统的长期维护。
Cypress相比Selenium的几个特点:
- 测试运行过程:在运行测试的时候,cypress会获取快照,记录了测试执行过程的每一步细节。
- 可调试性:支持使用web浏览器上的开发工具直接调试,有丰富错误和堆栈跟踪信息。
- 自动等待:在页面某些元素还没出来的时候,通常我们会添加等待的代码。但是在cypress中,是自动等待的,直到元素出现,或者超过了你设置的超时时间。
- Spies, Stubs, and Clocks: 这三样特性可以帮助我们更好的控制和确认功能的行为,比如服务的响应。
- 截图和视频:支持失败自动截图,还可以在运行测试的时候生成运行过程的视频。
- 跨浏览器测试:支持chrome、Firefox等多浏览器测试。
准备工作
1. node环境
在新版的Node.js中已将npm集成,cypress只需安装node环境便可使用,安装后可以查看下版本检验是否已安装成功
安装后查看node版本node –v
查看npm版本npm -v
2. 安装依赖
新建cypress项目文件夹,npm init
在项目文件夹下 npm install cypress --save-dev(npm i cypress -S -D)
3. 运行cypress
运行cypress可以多种方式,下面举出几种方式
- 进入项目目录下的...\node_modules.bin
命令行输入cypress open
- npx cypress open
- 在package.json文件中配置,scipts字段配置 cypress方式
cypress run 是以无头浏览器模式跑测试用例文件夹下的所有测试用例
cypress open 会打开测试用例集的界面,需要手动运行
{
"scripts": {
"cypress:run": "cypress run",
"cypress:open": "cypress open"
}
}
这样执行 npm run cypress就可以了
我们在修改的代码,这里会同步更新
点击相应的文件方可执行测试用例,并把执行的过程截屏记录下来
运行cypress open后,就init出来了cypress的项目目录结构
4. 目录结构
可以看到cypress文件夹下的结构
/cypress
/fixtures # mock数据的存储目录,这里存放了所有mock的json文件
- example.json
/integration # 测试用例代码目录
/examples
/plugins # 插件目录
- index.js
/support
- commands.js
- index.js
fixtures: 测试⽤例中需要⽤到的资源,包括测试数据、图⽚、json信息等,可以使⽤cy.fixture读取
integration: 测试脚本存放⽬录,允许多级⽬录,其下的example⽬录是官⽅提供的测试脚本样例,测试脚本的命名以.spec.js结尾。
cypress.json: Cypress的配置⽂件
package.json和package-lock.json npm初始化项⽬⾃动⽣成的⽂件
在..\cypress\integration文件夹下面可以自定义文件以(.spec.js)写测试脚本。
5.cypress.json 配置
官网上给出了详细的配置介绍,Configuration | Cypress Documentation
{
"baseUrl": "http://localhost:8000", // URL used as prefix for [`cy.visit()`] or [`cy.request()`] command's URL
"viewportWidth": 1920, // Default width in pixels for the application under tests' viewport. (Override with [`cy.viewport()`]
"viewportHeight": 1080, // Default height in pixels for the application under tests' viewport (Override with [`cy.viewport()`]
"defaultCommandTimeout":60000, // Time, in milliseconds, to wait until most DOM based commands are considered timed out
"requestTimeout":100000, // Time, in milliseconds, to wait for a request to go out in a [`cy.wait()`]
"responseTimeout":100000, // Time, in milliseconds, to wait until a response in a [`cy.request()`], [`cy.wait()`], [`cy.fixture()`], [`cy.getCookie()`], [`cy.getCookies()`], [`cy.setCookie()`], [`cy.clearCookie()`], [`cy.clearCookies()`], and [`cy.screenshot()`] commands
"watchForFileChanges":false, // Whether Cypress will watch and restart tests on test file changes 默认true
"video": false, // Whether Cypress will capture a video of the tests run with cypress run. 默认为true
"reporter": "junit", // The [reporter] used during `cypress run`
"ignoreTestFiles":"*.hot-update.js", //A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses `minimatch` with the options: `{dot: true, matchBase: true}`. We suggest using [https://globster.xyz](https://globster.xyz/) to test what files would match.
"reporterOptions": {
"mochaFile": "cypress/results/TestReport-[hash].xml",
"toConsole": true
} // The [reporter options] used. Supported options depend on the reporter.
}
6.测试报告
cypress有内置的测试报告格式,同时也支持用户自定义测试报告格式
内置的测试报告包括 Mocha 的内置测试报告和直接嵌入在 Cypress 中的测试报告,主要有以下几种
1.spec 格式报告
2.json 格式报告
3.junit 格式报告
spec 格式报告
spec 格式是 Mocha 的内置报告,它的输出是一个嵌套的分级视图
在 Cypress 中使用 spec 格式的报告,在命令行运行时加上--reporter=spec
json 格式报告
json 测试报告格式将输出一个大的 JSON 对象
在 Cypress 中使用 json 格式的报告,在命令行运行时加上--reporter=json
npm cypress:run --reporter=json --reporter-options "toConsole=true"”
junit格式报告
junit 测试报告格式输出一个 xml 文件
在 Cypress 中使用 xml 格式的报告,在命令行运行时加上--reporter=junit
npm cypress:run --reporter junit --reporter-options "mochaFile=results/test_output.xml,toConsole=true"
自定义格式
npm install --save-dev mocha
npm install --save-dev mochawesome
运行
npm cypress:run --reporter mochawesome
var mocha = require('mocha');
module.exports = MyReporter;
function MyReporter(runner) {
mocha.reporters.Base.call(this, runner)
var passes = 0
var failures = 0
runner.on('pass', function (test) {
passes++
console.log('pass:%s', test.fullTitle())
})
runner.on('fail', function (test, err) {
failures++
console.log('fail:%s -- error:%s', test.fullTitle(), err.message)
})
runner.on('end', function () {
console.log('用户自定义报告:%d/%d', passes, passes + failures)
})
}
运行
npm cypress:run --reporter ../cypress/reporters/custom_reporter.js
- 混合测试报告
Cypress 官方推荐使用 mocha-multi-reporters 来生成混合测试报告(GitHub - stanleyhlng/mocha-multi-reporters: Generate multiple mocha reports in a single mocha execution.)
npm install --save-dev mocha-multi-reporters mocha-junit-reporter
{
"reporterEnabled": "spec,json, mocha-junit-reporter",
"reporterOptions": {
"mochaFile": "cypress/results/results-[hash].xml"
}
}
运行
npm cypress run --reporter mocha-multi-reporters --reporter-options configFile=cypress/reporters/custom.json --spec cypress/integration/testLogin.js
定位方式
- cypress有三种专有定位方式,data-*属性,css或js改变不会影响测试(如果测试人员编写用例,需要与前端开发约定好属性的值)
data-cy
data-test
data-testid
例如为按钮添加data-属性
定位操作这个元素
先介绍下对与元素常用的几个操作
.click()点击
.type()输入
.clear()清空
定位元素用cy.get()
那点击“提交”按钮的动作可以写为
cy.get("[data-cy=submit]").click()
cy.get("[data-test=submit]").click()
cy.get("[data-testid=submit]").click()
当然也可以通过选择器取到dom
- id选择器
cy.get('#btn').click() - class选择器
cy.get('.btn').click()
*标签选择器
cy.get('button').click() - attributes属性选择器
["属性选择器"] ~= , |=,*= , ^= , $=
cy.get('button[type="button"]').click()
[attribute=“value”] 用于选取带有指定属性的元素。
[attribute=“value”] 用于选取带有指定属性和值的元素。
[attribute~=“value”] 用于选取属性值中包含指定词汇的元素。
[attribute|=“value”] 用于选取带有以指定值开头的属性值的元素,该值必须是整个单词。
[attribute^=“value”] 匹配属性值以指定值开头的每个元素。
[attribute$=“value”] 匹配属性值以指定值结尾的每个元素。
[attribute*=“value”] 匹配属性值中包含指定值的每个元素。
1.attribute属性中包含value:
[attribute~="value"] 属性中包含独立的单词为value
eg:[title~=flower]
[attribute*=value] 属性中做字符串拆分,只要能拆出来value这个词就行
eg:[title*=flower]
2.attribute属性以value开头:
[attribute|=value] 属性中必须是完整且唯一的单词,或者以-分隔开
eg:[lang|=en]
[attribute^=value] 属性的前几个字母是value就可以
e.g:[lang^=en]
3.attribute属性以value结尾:
[attribute$=value] 属性的后几个字母是value就可以
eg:[src$=".pdf"]
- 伪类 选择器
:nth-child(n)
cy.get('.btn:nth-child(2)')
:nth-of-type(n)
cy.get('.btn:nth-of-type(2)') - 后代选择器
cy.get('.main .btn') - 子元素选择器
cy.get('tbody > tr:nth-child(1) > th') - Cypress.('account') 等价于 cy.get('#account')
优先级
data-id>data-test>data-testid>id>class>tag>attributes>:nth-child
Cypress 页面元素基本操作方式
// 搜索定位元素
.get(selector)
// 搜索定位元素
.contains(selector)
// 搜索定位元素
.find(selector)
// 方法用来获取DON元素的子元素
.children()
// 用来获取DOM元素的所有父元素
.parents()
// 用来获取DOM元素第一层元素
.parent()
// 用来获取DOM元素的所有同级元素
.siblings()
// 用来获取指定DOM对象的第一个元素
.first()
// 用来获取指定DOM对象的最后一个元素
.last()
// 用来匹配DOM对象紧跟着的下一个同级元素
.next()
// 用来匹配给定的DOM对象的所有同级元素
.nextAll()
// 用来匹配给定DOM对象之后的所有同级元素直到遇到Until里定义的元素为止
.nextUntil()
// 用来匹配给定DOM对象紧跟着的上一个同级元素
.prev()
// 用来匹配给定的DOM对象之前的所有同级元素
.prevAll()
// 用来匹配给定DOM对象之后的所有同级元素直到遇到Until里定义的元素为止
.prevUntil()
// 用来遍历数组及其类似结果
.each()
// 用来在元素或者数组中的特定索引处获取DOM元素。类似于Jquery中nth:child()
.eq()
describe声明一个测试用例集
beforeEach 测试用例前置操作相当于setup
it声明了一个测试用例
cy.get() 定位元素 css selector定位选择器
type() 输入文本
should()类型断言
have.value元素的value属性
clear()清空文本
cy.creenshot()进行截图
cy.reload()刷新页面
cy.reload(true)强制刷新页面
cy.visit()访问网址
cy.url()获取当前页面url
cy.title()获取当前页面的title
cy.request()
cy.task()
cy.go()前进后退
cy.exec()
cy.fixture()
cy.getCookies()
cy.getCookies()
cy.setCookie()
cy.clearCookie()
cy.clearCookies()
cy.screenshot()
cy.wait()
cy.viewport()设置窗口
Cypress 常见操作
访问某个 link cy.visit()
//访问百度
cy.visit('http://www.baidu.com)
获取当前页面 URL cy.url()
//获取页面地址
cy.url();
cy.url().should("contain", "baidu");
刷新页面 cy.reload()
// 等同于 F5
cy.reaload();
// 等同于 ctrl+F5 强制刷新
cy.radload(true);
设置窗口 cy.viewport()
//在 cypress.json 中添加
{
'viewportWidth':'1000',
'viewportHeight':'600'
}
//运行中设置
cy.viewport(1024,768)
前进后退 cy.go()
//后退
cy.go('back)
cy.go(-1)
//前进
cy.go('forward)
cy.go(1)
判断元素是否存在 should()
//判断 check-box 是否可见
cy.get('.check-box).should('be.visible')
//判断元素存在
cy.get('.check-box).should('exist')
//判断元素不存在
cy.get('.check-box).should('no exist')
条件判断
//利用 jquery 来判断元素是否存在
const btn = '#btn'
Cypress.$(btn).length>0{
cy.get(btn).click()
}
获取元素属性值
//获取元素 btn 的文本
cy.get("#btn").then(function () {
const btnTxt = $btn.text();
cy.log(btnTxt);
});
清除文本 clear()
//清除 input 输入的值
cy.get("div>a").clear();
cy.get("div>a").clear().type();
操作单选/多选按钮
//选中
cy.get("radio").check("us");
//取消选中
cy.get("radio").uncheck("us");
操作下拉菜单
//获取页面地址
cy.get("select").select("下拉选项的值");
cy.get("li").eq(0).click();
操作弹出框
//获取页面地址
cy.get("iframe").then(function ($iframe) {
//定义要查找的元素
const $body = $iframe.contents().find("body");
//在查找到的元素中查找btn并单击
cy.wrap($body).find("#bin").click();
});
操作被覆盖的元素
cy.get("#btn").click({ force: true });
模拟键盘操作 type()
cy.get("input").type("111");
cy.get("input").type("{enter}");