前端自动化测试框架Cypress之基础知识

一、Cypress目录及环境搭建

(一)、安装Cypress

  • 在线安装使用npm
    npm install cypress --save-dev
  • 离线安装
    第一步,官网上下载cpress.zip压缩包 ☟点击下载
    第二步,配置系统环境变量
    CYPRESS_INSTALL_BINARY 下载并安装的Cypress二进制文件的目标位置
    第三步,执行安装命令
    CYPRESS_INSTALL_BINARY=2.0.1 npm install cypress@2.0.3
    ☞官网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('fake@email.com')
      .should('have.value', 'fake@email.com')
  })
})

二、运行案例测试

  • 配置指令
    "test:e2e-gui": "vue-cli-service test:e2e --mode development"
  • 页面操作
    在这里插入图片描述
    点击需要测试文件如下
    在这里插入图片描述

三、Cypress闭包环境变量

1. describe

describe(’’, () => {
// 各种钩子函数
before(() => {
// 在全部案例执行之前执行一次
})
after(() => {
// 在全部案例执行完成之后执行一次
})
beforeEach(() => {
// 在每一条案例之前执行
})
afterEach(() => {
// 在每一条案例执行完成之后执行
})
it(‘测试表单验证案例’, () => {})
// 只执行该案例
it.only(‘测试表单验证案例’, () => {})
// 忽略该案例
it.skip(‘测试表单验证案例’, () => {})
})

2. 钩子函数里环境变量

  1. 可以访问cy对象
    传入selector选择器,本质是由jquery选择器实现,故用法一致
    const $cyElement = cy.get('.demo-ruleForm > .el-form-item:first-child input')
    
  2. 类似jquery可以链式调用
    样式选择器连接如是
cy.get('.demo-ruleForm > .el-form-item:first-child input')
     .should('have.value', '')
     .focus()
     .blur()
  1. js基本变量语法
    js基本语法api都支持,但稍有不同
 const arr = [];
 arr.forEach 可以访问
 [].forEach 不可以访问
  1. expect asset断言
assert.isOk('everything', 'everything is ok')
expect('Jane').to.not.equal('Jane')
  1. 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断言

  1. Length
// retry until we find 3 matching <li.selected>
cy.get('li.selected').should('have.length', 3)
  1. Class
// retry until this input does not have class disabled
cy.get('form').find('input').should('not.have.class', 'disabled')
  1. Value
// retry until this textarea has the correct value
cy.get('textarea').should('have.value', 'foo bar baz')
  1. Text Content
// retry until this span does not contain 'click me'
cy.get('a').parent('span.help').should('not.contain', 'click me')
  1. Visibility
// retry until this button is visible
cy.get('button').should('be.visible')
  1. Existence
// retry until loading spinner no longer exists
cy.get('#loading').should('not.exist')
  1. State
// retry until our radio is checked
cy.get(':radio').should('be.checked')
  1. 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对象时

ChainerExample
notexpect(name).to.not.equal(‘Jane’)
deepexpect(obj).to.deep.equal({ name: ‘Jane’ })
nestedexpect({a: {b: [‘x’, ‘y’]}}).to.have.nested.property(‘a.b[1]’) expect({a: {b: [‘x’, ‘y’]}}).to.nested.include({‘a.b[1]’: ‘y’})
orderedexpect([1, 2]).to.have.ordered.members([1, 2]).but.not.have.ordered.members([2, 1])
anyexpect(arr).to.have.any.keys(‘age’)
allexpect(arr).to.have.all.keys(‘name’, ‘age’)
a(type) Aliases: anexpect(‘test’).to.be.a(‘string’)
include(value) Aliases: contain, includes, containsexpect([1,2,3]).to.include(2)
okexpect(undefined).to.not.be.ok
trueexpect(true).to.be.true
falseexpect(false).to.be.false
nullexpect(null).to.be.null
undefinedexpect(undefined).to.be.undefined
existexpect(myVar).to.exist
emptyexpect([]).to.be.empty
arguments Aliases: Argumentsexpect(arguments).to.be.arguments
equal(value) Aliases: equals, eqexpect(42).to.equal(42)
deep.equal(value)expect({ name: ‘Jane’ }).to.deep.equal({ name: ‘Jane’ })
eql(value) Aliases: eqlsexpect({ name: ‘Jane’ }).to.eql({ name: ‘Jane’ })
greaterThan(value) Aliases: gt, aboveexpect(10).to.be.greaterThan(5)
least(value)Aliases: gteexpect(10).to.be.at.least(10)
lessThan(value) Aliases: lt, belowexpect(5).to.be.lessThan(10)
most(value) Aliases: lteexpect(‘test’).to.have.length.of.at.most(4)
within(start, finish)expect(7).to.be.within(5,10)
instanceOf(constructor) Aliases: instanceofexpect([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.propertyexpect(‘test’).to.have.ownProperty(‘length’)
ownPropertyDescriptor(name) Aliases: haveOwnPropertyDescriptorexpect({a: 1}).to.have.ownPropertyDescriptor(‘a’)
lengthOf(value)expect(‘test’).to.have.lengthOf(3)
match(RegExp) Aliases: matchesexpect(‘testing’).to.match(/^test/)
string(string)expect(‘testing’).to.have.string(‘test’)
keys(key1, [key2], […]) Aliases: keyexpect({ pass: 1, fail: 2 }).to.have.keys(‘pass’, ‘fail’)
throw(constructor) Aliases: throws, Throwexpect(fn).to.throw(Error)
respondTo(method) Aliases: respondsToexpect(obj).to.respondTo(‘getName’)
itselfexpect(Foo).itself.to.respondTo(‘bar’)
satisfy(method) Aliases: satisfiesexpect(1).to.satisfy((num) => { return num > 0 })
closeTo(expected, delta) Aliases: approximatelyexpect(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: changesexpect(fn).to.change(obj, ‘val’)
increase(function) Aliases: increasesexpect(fn).to.increase(obj, ‘val’)
decrease(function) Aliases: decreasesexpect(fn).to.decrease(obj, ‘val’)

2. 断言入参是dom对象时

ChainersAssertion
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(‘test@dev.com’)
visibleexpect($el).to.be.visible
hiddenexpect($el).to.be.hidden
selectedexpect($option).not.to.be.selected
checkedexpect($input).not.to.be.checked
focus[ed]expect($input).not.to.be.focused expect($input).to.have.focus
enabledexpect($input).to.be.enabled
disabledexpect($input).to.be.disabled
emptyexpect($el).not.to.be.empty
existexpect($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/methodAssertion
calledexpect(spy).to.be.called
callCountexpect(spy).to.have.callCount(n)
calledOnceexpect(spy).to.be.calledOnce
calledTwiceexpect(spy).to.be.calledTwice
calledThriceexpect(spy).to.be.calledThrice
calledBeforeexpect(spy1).to.be.calledBefore(spy2)
calledAfterexpect(spy1).to.be.calledAfter(spy2)
calledWithNewexpect(spy).to.be.calledWithNew
alwaysCalledWithNewexpect(spy).to.always.be.calledWithNew
calledOnexpect(spy).to.be.calledOn(context)
alwaysCalledOnexpect(spy).to.always.be.calledOn(context)
calledWithexpect(spy).to.be.calledWith(…args)
alwaysCalledWithexpect(spy).to.always.be.calledWith(…args)
calledWithExactlyexpect(spy).to.be.calledWithExactly(…args)
alwaysCalledWithExactlyexpect(spy).to.always.be.calledWithExactly(…args)
calledWithMatchexpect(spy).to.be.calledWithMatch(…args)
alwaysCalledWithMatchexpect(spy).to.always.be.calledWithMatch(…args)
returnedexpect(spy).to.have.returned(returnVal)
alwaysReturnedexpect(spy).to.have.always.returned(returnVal)
threwexpect(spy).to.have.thrown(errorObjOrErrorTypeStringOrNothing)
alwaysThrewexpect(spy).to.have.always.thrown(errorObjOrErrorTypeStringOrNothing)

(三)、assert断言

AssertionExample
.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’)

八、项目常见报错解决方案

后续持续更新。。。

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值