前端自动化测试框架Cypress之基础知识
一、Cypress目录及环境搭建
(一)、安装Cypress
- 在线安装使用npm
npm install cypress --save-dev
- 离线安装
第一步,官网上下载cpress.zip压缩包 ☟点击下载
第二步,配置系统环境变量
CYPRESS_INSTALL_BINARY 下载并安装的Cypress二进制文件的目标位置
第三步,执行安装命令
CYPRESS_INSTALL_BINARY=2.0.1 npm install [email protected]
☞官网api
(二)、目录结构
- support文件夹中存,指定集合
/**
- 编写人:
- 日期:
- 目的:登录
- 参数说明:
*/
Cypress.Commands.add("login", (userName, password) => {
cy.visit('/#/login-page')
cy.get("#app > div input").type(userName)
cy.get("#secpwd").type(password)
cy.get("#app > button").click()
})
- specs目录中存放测试案例
describe('My First Test', function() {
before(() => {
// 在全部案例执行之前执行一次
// 浏览器模式访问时,可按照自己的需要设置窗口大小,如 cy.viewport(1920,1100)
// 访问首页
cy.visit('http://localhost:2000/#/input-demo')
})
after(() => {
// 在全部案例执行完成之后执行一次
})
beforeEach(() => {
// 在每一条案例之前执行
})
afterEach(() => {
// 在每一条案例执行完成之后执行
})
it('Does not do much!', function() {
expect(true).to.equal(false)
})
it('Gets, types and asserts', function() {
cy.visit('https://example.cypress.io')
cy.contains('type').click()
// 应该存在一个包含'/commands/actions'的新URL
cy.url().should('include', '/commands/actions')
// 获取一个输入, 输入进去并且验证文本值已经更新了
cy.get('.action-email')
.type('[email protected]')
.should('have.value', '[email protected]')
})
})
二、运行案例测试
- 配置指令
"test:e2e-gui": "vue-cli-service test:e2e --mode development"
- 页面操作
点击需要测试文件如下
三、Cypress闭包环境变量
1. describe
describe(’’, () => {
// 各种钩子函数
before(() => {
// 在全部案例执行之前执行一次
})
after(() => {
// 在全部案例执行完成之后执行一次
})
beforeEach(() => {
// 在每一条案例之前执行
})
afterEach(() => {
// 在每一条案例执行完成之后执行
})
it(‘测试表单验证案例’, () => {})
// 只执行该案例
it.only(‘测试表单验证案例’, () => {})
// 忽略该案例
it.skip(‘测试表单验证案例’, () => {})
})
2. 钩子函数里环境变量
- 可以访问cy对象
传入selector选择器,本质是由jquery选择器实现,故用法一致const $cyElement = cy.get('.demo-ruleForm > .el-form-item:first-child input')
- 类似jquery可以链式调用
样式选择器连接如是
cy.get('.demo-ruleForm > .el-form-item:first-child input')
.should('have.value', '')
.focus()
.blur()
- js基本变量语法
js基本语法api都支持,但稍有不同
const arr = [];
arr.forEach 可以访问
[].forEach 不可以访问
- expect asset断言
assert.isOk('everything', 'everything is ok')
expect('Jane').to.not.equal('Jane')
- Cypress对象
Cypress提供了cy的一切api,另外多了.$()方法用于返回jquery对象
便于操作dom
3. should里环境变量
可以访问Cypress对象
js基本变量语法
const $cyElement = cy.get('.demo-ruleForm > .el-form-item:first-child input')
$cyElement.should($el => {
// 闭包环境里可以访问Cypress是个可以操作dom集合
// Cypress.dom 返回dom的api
// Cypress.$(dom) 返回jquery对象
const isDom = Cypress.dom.isDom($el)
const $jqEl = Cypress.$($el[0])
expect(isDom).to.be.true
})
- Cypress.$()返回jQuery对象,可以访问jquery 的api
- Cypress.dom 返回dom的api
- Cpress.cy返回cy实例,可以类似钩子函数里断言
4. 案例
代码如下:
// https://docs.cypress.io/api/introduction/api.html
describe('自动化测试表单验证案例', () => {
before(() => {
// 在全部案例执行之前执行一次
// 浏览器模式访问时,可按照自己的需要设置窗口大小,如 cy.viewport(1920,1100)
// 访问首页
cy.visit('http://localhost:2000/#/input-demo')
})
after(() => {
// 在全部案例执行完成之后执行一次
})
beforeEach(() => {
// 在每一条案例之前执行
})
afterEach(() => {
// 在每一条案例执行完成之后执行
})
// 案例一、测试表单验证案例
it('测试表单验证案例', () => {
cy.get('.demo-ruleForm > .el-form-item:first-child input')
.should('have.value', '')
.focus()
.blur()
cy.get('.demo-ruleForm .el-form-item__error')
.should('contain', '请输入活动名称')
cy.get('.demo-ruleForm > .el-form-item:first-child input').type('测试数据')
.blur()
cy.get('.demo-ruleForm .el-form-item__error')
.should('not.be.visible')
cy.get('.demo-ruleForm > .el-form-item:first-child input').type('测试数据测试数据')
.blur()
cy.get('.demo-ruleForm .el-form-item__error')
.should('contain', '长度在 3 到 5 个字符')
})
// 案例二、测试一些闭包环境变量及api
it('测试表单验证案例', () => {
// This is fine, jQuery returns the element synchronously.
// const $jqElement = $('.demo-ruleForm > .el-form-item:first-child input')
// This will not work! Cypress does not return the element synchronously.
const $cyElement = cy.get('.demo-ruleForm > .el-form-item:first-child input')
// cy操作类型jquery传入selector选择器,但不可以直接操作dom
// 类似jquery可以链式调用
// js基本变量语法
// 但稍有不同
const arr = [];
// arr.forEach 可以访问
// [].forEach 不可以访问
$cyElement.should($el => {
// 闭包环境里可以访问Cypress是个可以操作dom集合
// Cypress.dom 返回dom的api
// Cypress.$(dom) 返回jquery对象
const isDom = Cypress.dom.isDom($el)
const $jqEl = Cypress.$($el[0])
expect(isDom).to.be.true
})
})
})
四、获取dom元素
1.get
用法一、选择器定位
cy.get(selector)
用法二、以别名定位
cy.get(alias)
匹配多个元素时,返回多个对象
2.find
cy.get(selector).find(selector1)
定位方法,用来在 DOM 树中搜索已被定位到的元素的后代,例如get选中的后代元素
// 错误写法
cy.find(selector)
会报错,需要一个父类选中元素
3.contains
两种用法,如下:
.contains(content)
.contains(selector, content)
支持cy.contains,及cy.get().contains
重点:只会返回第一个匹配到的元素
4.first
类似jquery的first方法
5.eq
类似jquery的eq方法,eq(index) index从0开始获取第几个元素子元素
五、获取/设置dom元素属性 !!!
1. cy.$$(selector,context) ☆☆☆
cy.$$('input') // 返回input元素的jquery对象
cy.$$('input').value() // 可用用jquery的api
cy.$$('input').text() // 例子,等等
cy对象挂载在window下,故环境变量this指向window时可用访问,一般顶级作用域指向这个window,可用通过箭头函数将内部作用域指向window,访问cy
2. Cypress.$(selector) ☆☆☆
Cypress.$('input') // 返回input元素的jquery对象
Cypress.$('input').value() // 可用用jquery的api
Cypress.$('input').text() // 例子,等等
Cypress对象挂载在window下,故环境变量this指向window时可用访问,一般顶级作用域指向这个window,可用通过箭头函数将内部作用域指向window,访问Cypresss
3.should回调函数 ☆☆☆
- 形参$e为jquery对象
- Cpress.$ 返回jquery对象
利用jquery的api可以获取元素的dom属性
cy.get('.dom-demo')
.should($e => {
// $e jquery对象
// Cpress.$ 返回jquery对象
const text = $e.find('.item').first().text();
const text1 = Cypress.$($e[0]).find('.item').first().text();
console.log(text === text1) // true
})
4.cy对象的type方法
对input输入框的输入
cy.get('input').type('text')
cy.get('input').type('2222')
六、鼠标事件
1.单击事件click
// 点击事件
cy.get('#btn').click()
2.悬浮事件hover
版本不支持该事件,有代替方案如下:
cy.get('#btn')
.trigger('mouserover')
.wait(3000)
.rightclick()
.wait(3000)
.should('have.css', 'color', 'rgb(255, 255, 255)')
3.双加事件dblclick
// 双击事件
cy.get('#btn').dblclick()
4.右击事件rightclick
// 右击事件
cy.get('#btn').rightclick()
5.聚焦事件focus
// 聚焦事件
cy.get('input').focus()
6.失焦事件blur
// 失焦事件
cy.get('input').blur()
总结案例
// 案例三、测试鼠标一些事件
it('测试鼠标一些事件', () => {
// 点击事件
cy.get('#btn')
.click()
// 双击事件
cy.get('#btn')
.dblclick()
// 右击事件
cy.get('#btn')
.rightclick()
// 悬浮事件
// cy.get('#btn').hover()
// 该事件本版本不支持
// 代替方案如下:
cy.get('#btn')
.trigger('mouserover')
.wait(3000)
.rightclick()
.wait(3000)
.should('have.css', 'color', 'rgb(255, 255, 255)')
// 聚焦事件
cy.get('#int')
.focus()
// 失焦事件
cy.get('#int')
.blur()
})
七、断言should、expect
每条案例都需要加断言,及验证点,否则是一条不完整的案例
(一)、should断言
1. dom断言
- Length
// retry until we find 3 matching <li.selected>
cy.get('li.selected').should('have.length', 3)
- Class
// retry until this input does not have class disabled
cy.get('form').find('input').should('not.have.class', 'disabled')
- Value
// retry until this textarea has the correct value
cy.get('textarea').should('have.value', 'foo bar baz')
- Text Content
// retry until this span does not contain 'click me'
cy.get('a').parent('span.help').should('not.contain', 'click me')
- Visibility
// retry until this button is visible
cy.get('button').should('be.visible')
- Existence
// retry until loading spinner no longer exists
cy.get('#loading').should('not.exist')
- State
// retry until our radio is checked
cy.get(':radio').should('be.checked')
- CSS
// retry until .completed has matching css
cy.get('.completed').should('have.css', 'text-decoration', 'line-through')
// retry until .accordion css have display: none
cy.get('#accordion').should('not.have.css', 'display', 'none')
2. 回调函数
cy.get('div')
.should(($div) => {
expect($div).to.have.length(1)
const className = $div[0].className
// className will be a string like "main-abc123 heading-xyz987"
expect(className).to.match(/heading-/)
})
(二)、expect断言
1. 断言入参不是dom对象时
Chainer | Example |
---|---|
not | expect(name).to.not.equal(‘Jane’) |
deep | expect(obj).to.deep.equal({ name: ‘Jane’ }) |
nested | expect({a: {b: [‘x’, ‘y’]}}).to.have.nested.property(‘a.b[1]’) expect({a: {b: [‘x’, ‘y’]}}).to.nested.include({‘a.b[1]’: ‘y’}) |
ordered | expect([1, 2]).to.have.ordered.members([1, 2]).but.not.have.ordered.members([2, 1]) |
any | expect(arr).to.have.any.keys(‘age’) |
all | expect(arr).to.have.all.keys(‘name’, ‘age’) |
a(type) Aliases: an | expect(‘test’).to.be.a(‘string’) |
include(value) Aliases: contain, includes, contains | expect([1,2,3]).to.include(2) |
ok | expect(undefined).to.not.be.ok |
true | expect(true).to.be.true |
false | expect(false).to.be.false |
null | expect(null).to.be.null |
undefined | expect(undefined).to.be.undefined |
exist | expect(myVar).to.exist |
empty | expect([]).to.be.empty |
arguments Aliases: Arguments | expect(arguments).to.be.arguments |
equal(value) Aliases: equals, eq | expect(42).to.equal(42) |
deep.equal(value) | expect({ name: ‘Jane’ }).to.deep.equal({ name: ‘Jane’ }) |
eql(value) Aliases: eqls | expect({ name: ‘Jane’ }).to.eql({ name: ‘Jane’ }) |
greaterThan(value) Aliases: gt, above | expect(10).to.be.greaterThan(5) |
least(value)Aliases: gte | expect(10).to.be.at.least(10) |
lessThan(value) Aliases: lt, below | expect(5).to.be.lessThan(10) |
most(value) Aliases: lte | expect(‘test’).to.have.length.of.at.most(4) |
within(start, finish) | expect(7).to.be.within(5,10) |
instanceOf(constructor) Aliases: instanceof | expect([1, 2, 3]).to.be.instanceOf(Array) |
property(name, [value]) | expect(obj).to.have.property(‘name’) |
deep.property(name, [value]) | expect(deepObj).to.have.deep.property(‘tests[1]’, ‘e2e’) |
ownProperty(name) Aliases: haveOwnProperty, own.property | expect(‘test’).to.have.ownProperty(‘length’) |
ownPropertyDescriptor(name) Aliases: haveOwnPropertyDescriptor | expect({a: 1}).to.have.ownPropertyDescriptor(‘a’) |
lengthOf(value) | expect(‘test’).to.have.lengthOf(3) |
match(RegExp) Aliases: matches | expect(‘testing’).to.match(/^test/) |
string(string) | expect(‘testing’).to.have.string(‘test’) |
keys(key1, [key2], […]) Aliases: key | expect({ pass: 1, fail: 2 }).to.have.keys(‘pass’, ‘fail’) |
throw(constructor) Aliases: throws, Throw | expect(fn).to.throw(Error) |
respondTo(method) Aliases: respondsTo | expect(obj).to.respondTo(‘getName’) |
itself | expect(Foo).itself.to.respondTo(‘bar’) |
satisfy(method) Aliases: satisfies | expect(1).to.satisfy((num) => { return num > 0 }) |
closeTo(expected, delta) Aliases: approximately | expect(1.5).to.be.closeTo(1, 0.5) |
members(set) | expect([1, 2, 3]).to.include.members([3, 2]) |
oneOf(values) | expect(2).to.be.oneOf([1,2,3]) |
change(function) Aliases: changes | expect(fn).to.change(obj, ‘val’) |
increase(function) Aliases: increases | expect(fn).to.increase(obj, ‘val’) |
decrease(function) Aliases: decreases | expect(fn).to.decrease(obj, ‘val’) |
2. 断言入参是dom对象时
Chainers | Assertion |
---|---|
attr(name, [value]) | expect($el).to.have.attr(‘foo’, ‘bar’) |
prop(name, [value]) | expect($el).to.have.prop(‘disabled’, false) |
css(name, [value]) | expect($el).to.have.css(‘background-color’, ‘rgb(0, 0, 0)’) |
data(name, [value]) | expect($el).to.have.data(‘foo’, ‘bar’) |
class(className) | expect($el).to.have.class(‘foo’) |
id(id) | expect($el).to.have.id(‘foo’) |
html(html) | expect($el).to.have.html(‘I love testing’) |
text(text) | expect($el).to.have.text(‘I love testing’) |
value(value) | expect($el).to.have.value(‘[email protected]’) |
visible | expect($el).to.be.visible |
hidden | expect($el).to.be.hidden |
selected | expect($option).not.to.be.selected |
checked | expect($input).not.to.be.checked |
focus[ed] | expect($input).not.to.be.focused expect($input).to.have.focus |
enabled | expect($input).to.be.enabled |
disabled | expect($input).to.be.disabled |
empty | expect($el).not.to.be.empty |
exist | expect($nonexistent).not.to.exist |
match(selector) | expect($emptyEl).to.match(’:empty’) |
contain(text) | expect($el).to.contain(‘text’) |
descendants(selector) | expect($el).to.have.descendants(‘div’) |
3.断言对象是 cy.stub() 或 cy.spy()时
Sinon.JS property/method | Assertion |
---|---|
called | expect(spy).to.be.called |
callCount | expect(spy).to.have.callCount(n) |
calledOnce | expect(spy).to.be.calledOnce |
calledTwice | expect(spy).to.be.calledTwice |
calledThrice | expect(spy).to.be.calledThrice |
calledBefore | expect(spy1).to.be.calledBefore(spy2) |
calledAfter | expect(spy1).to.be.calledAfter(spy2) |
calledWithNew | expect(spy).to.be.calledWithNew |
alwaysCalledWithNew | expect(spy).to.always.be.calledWithNew |
calledOn | expect(spy).to.be.calledOn(context) |
alwaysCalledOn | expect(spy).to.always.be.calledOn(context) |
calledWith | expect(spy).to.be.calledWith(…args) |
alwaysCalledWith | expect(spy).to.always.be.calledWith(…args) |
calledWithExactly | expect(spy).to.be.calledWithExactly(…args) |
alwaysCalledWithExactly | expect(spy).to.always.be.calledWithExactly(…args) |
calledWithMatch | expect(spy).to.be.calledWithMatch(…args) |
alwaysCalledWithMatch | expect(spy).to.always.be.calledWithMatch(…args) |
returned | expect(spy).to.have.returned(returnVal) |
alwaysReturned | expect(spy).to.have.always.returned(returnVal) |
threw | expect(spy).to.have.thrown(errorObjOrErrorTypeStringOrNothing) |
alwaysThrew | expect(spy).to.have.always.thrown(errorObjOrErrorTypeStringOrNothing) |
(三)、assert断言
Assertion | Example |
---|---|
.isOk(object, [message]) | assert.isOk(‘everything’, ‘everything is ok’) |
.isNotOk(object, [message]) | assert.isNotOk(false, ‘this will pass’) |
.equal(actual, expected, [message]) | assert.equal(3, 3, ‘vals equal’) |
.notEqual(actual, expected, [message]) | assert.notEqual(3, 4, ‘vals not equal’) |
.strictEqual(actual, expected, [message]) | assert.strictEqual(true, true, ‘bools strict eq’) |
.notStrictEqual(actual, expected, [message]) | assert.notStrictEqual(5, ‘5’, ‘not strict eq’) |
.deepEqual(actual, expected, [message]) | assert.deepEqual({ id: ‘1’ }, { id: ‘1’ }) |
.notDeepEqual(actual, expected, [message]) | assert.notDeepEqual({ id: ‘1’ }, { id: ‘2’ }) |
.isAbove(valueToCheck, valueToBeAbove, [message]) | assert.isAbove(6, 1, ‘6 greater than 1’) |
.isAtLeast(valueToCheck, valueToBeAtLeast, [message]) | assert.isAtLeast(5, 2, ‘5 gt or eq to 2’) |
.isBelow(valueToCheck, valueToBeBelow, [message]) | assert.isBelow(3, 6, ‘3 strict lt 6’) |
.isAtMost(valueToCheck, valueToBeAtMost, [message]) | assert.isAtMost(4, 4, ‘4 lt or eq to 4’) |
.isTrue(value, [message]) | assert.isTrue(true, ‘this val is true’) |
.isNotTrue(value, [message]) | assert.isNotTrue(‘tests are no fun’, ‘val not true’) |
.isFalse(value, [message]) | assert.isFalse(false, ‘val is false’) |
.isNotFalse(value, [message]) | assert.isNotFalse(‘tests are fun’, ‘val not false’) |
.isNull(value, [message]) | assert.isNull(err, ‘there was no error’) |
.isNotNull(value, [message]) | assert.isNotNull(‘hello’, ‘is not null’) |
.isNaN(value, [message]) | assert.isNaN(NaN, ‘NaN is NaN’) |
.isNotNaN(value, [message]) | assert.isNotNaN(5, ‘5 is not NaN’) |
.exists(value, [message]) | assert.exists(5, ‘5 is not null or undefined’) |
.notExists(value, [message]) | assert.notExists(null, ‘val is null or undefined’) |
.isUndefined(value, [message]) | assert.isUndefined(undefined, ‘val is undefined’) |
.isDefined(value, [message]) | assert.isDefined(‘hello’, ‘val has been defined’) |
.isFunction(value, [message]) | assert.isFunction(x => x * x, ‘val is func’) |
.isNotFunction(value, [message]) | assert.isNotFunction(5, ‘val not funct’) |
.isObject(value, [message]) | assert.isObject({num: 5}, ‘val is object’) |
.isNotObject(value, [message]) | assert.isNotObject(3, ‘val not object’) |
.isArray(value, [message]) | assert.isArray([‘unit’, ‘e2e’], ‘val is array’) |
.isNotArray(value, [message]) | assert.isNotArray(‘e2e’, ‘val not array’) |
.isString(value, [message]) | assert.isString(‘e2e’, ‘val is string’) |
.isNotString(value, [message]) | assert.isNotString(2, ‘val not string’) |
.isNumber(value, [message]) | assert.isNumber(2, ‘val is number’) |
.isNotNumber(value, [message]) | assert.isNotNumber(‘e2e’, ‘val not number’) |
.isFinite(value, [message]) | assert.isFinite(‘e2e’, ‘val is finite’) |
.isBoolean(value, [message]) | assert.isBoolean(true, ‘val is bool’) |
.isNotBoolean(value, [message]) | assert.isNotBoolean(‘true’, ‘val not bool’) |
.typeOf(value, name, [message]) | assert.typeOf(‘e2e’, ‘string’, ‘val is string’) |
.notTypeOf(value, name, [message]) | assert.notTypeOf(‘e2e’, ‘number’, ‘val not number’) |
八、项目常见报错解决方案
后续持续更新。。。