自动化测试框架[Cypress测试用例]

前提

Mocha简介

Cypress底层依赖于很多优秀的开源测试库,其中比较重要的就是Mocha,它是一个适用于Node.js和浏览器的测试框架,它使得异步测试变得简单灵活;而JavaScript是单线程异步执行的,这就产生了一种复杂的场景,因为异步往往无法直接判断函数的返回值是否符合预期,要验证异步函数的正确与否,就需要测试框架支持回调,利用Promise或者其他方式来验证异步函数的正确性,而Mocha就提供了出色的异步支持包括Promise

Cypress继承并扩展了Mocha对异步的支持,而Mocha提供了多种接口来定义测试套件,Hooks和Individual tests,即BDD、TDD、Exports、Qunit和Require

Cypress采用了Mocha的BDD语法,在Mocha中一个BDD风格的测试用例如下代码所示

describe('列表单元测试', function(){
	before(function(){
		// 实际测试前的准备工作
	});
	describe('#indexOf()', function(){
		context('当元素找不到时', function(){
			it('不应该抛出异常', function(){
				(function(){
					[1,2,3].indexOf(4);
					}.should.not.throw());
			});
			it('应该返回-1', function(){
				[1,2,3].indexOf(4).should.equal(-1);
			});
		});
		context('当元素找不到时', function(){
			it('应该返回该元素第一次在列表中出现的位置', function(){
				[1,2,3].indexOf(3).should.equal(2);
			});
		});
	});
	beafter(function(){
		// 测试结束后的收尾工作
	});
});

Cypress将Mocha硬编码在框架中,因此在Cypress中编写的所有测试用例都基于Mocha提供的如下基本模块

  • describe(): 测试套件,在其中可以设定context(),可以包含多个测试用例it(),也可以嵌套测试套件
  • it():用于描述测试用例,一个测试套件可以不包括任何钩子函数Hook,但必须包含至少一个测试用例it()
  • context():是decribe()的别名,其行为方式与describe()相同,使用context()只是提供一种使测试更易于阅读和组织的方式
  • before()
  • beforeEach()
  • afterEach()
  • after()
  • .only()
  • .skip()

对于一条可执行的测试而言,describe()和it()是两个必要的组成部分,除了这两个功能模块外,其他功能模块对于一条可执行的测试来说都是可选的

Hook函数

Hook约定俗成的被称为钩子函数,Mocha提供了4中Hook函数

  • before():所有测试用例的统一前置动作,before()在一个describe()内只会执行一次,执行顺序是在所有的测试用例it()之前
  • after():所有测试用例的统一后置动作,after()在一个describe()内只会执行一次,执行顺序是在所有的测试用例it()之后
  • beforeEach():每个测试用例的前置动作,在每个测试用例执行前执行,一个describe()内,有多少个it()就会执行多少次该函数
  • afterEach():每个测试用例的后置动作,在每个测试用例执行后执行,一个describe()内,有多少个it()就会执行多少次该函数

如果熟悉Junit和Unittest或者Pytest,那么从函数名字就能看出这几个Hook函数的作用,利用Hook函数可以在测试开始时设置测试的前提条件,测试结束后做一些清理工作,实例代码如下

describe('钩子函数', function(){
	before(function(){
		// 当前测试中,所有测试用例执行之前运行
	});
	after(function(){
		// 当前测试中,所有测试用例执行之后运行
	});
	beforeEach(function(){
		// 当前测试中,每个测试用例执行前运行
	});
	afterEach(function(){
		// 当前测试中,每个测试用例执行后运行
	});
	// 测试用例
});

测试用例的排除和包含

.skip()

排除测试套件/测试用例可以使用功能模块.skip(),在使用该功能特性的时候只需要在不想执行的describe()、context()、it()后跟.skip()即可,如下代码所示

describe.skip()
context.skip()
it.skip()

.only()

包含测试套件/测试用例可以使用功能模块.only(),当该特性出现时候,只有被他装饰的测试套件或用例会被执行,未被他装饰的不会被执行即便没有.skip(),如下代码所示

describe.only()
context.only()
it.only()

注意在使用.skip()和.only()装饰的时候,describe()优先级高于it()

动态忽略

///<reference types="cypress"/>
describe('登陆', function(){
	const username='davie.yang'
	const password='Ms123!@#'
	context('HTML Table Login Testing', function(){
		it('登陆成功,跳转dashboard', function(){
			if(Cypress.env('runFlag')===1){
				cy.visit('http://127.0.0.1:7077/login')
				cy.get('input[name=username]').type(username)
				cy.get('input[name=password]').type(password)
				cy.get('form').submit()
				cy.get('h1').should('contain', 'davie.yang')
			}
			else{
				this.skip()
				cy.log("runFlag为0,用例不执行")
			}
		})	
	})
})

然后输入命令执行该用例

yarn cypress:open -env runFlag=0

然后在Test Runner中选择该用例文件执行,则可以看到测试用例未被执行,因为命令中设定了runFlag为0,并且在this.skip()后的语句也不会被执行,this.skip()表示终止这条测试用例,所以后面的log语句也未执行

动态生成测试用例

所谓动态生成测试用例,也就是在Selenium中的数据驱动,测试步骤相同只是测试输入和输出不同,在Cypress中同样支持该特性,新建js文件,例如命名为testLogin.data.js用于存放测试输入

export const testLoginUser=[
	{
		summary:"Login pass",
		username:"davie.yang",
		password:"Ms123!@#"
	},
	{
		summary:"Login fail",
		username:"davie.yang1",
		password:"Ms123!@#1"
	},
]

在同级路径下创建测试文件,写入如下内容

///<reference types="cypress"/>
import{testLoginUser} from '../xxx/testLogin.data'
describe('登陆', function(){
	const username='davie.yang',
	const password='Ms123!@#',
	context('HTML Table Login Testing', functino(){
		for(const user of testLoginUser){
			it(user.summary, function(){
				cy.visit('http://localhost:7077/login')
				cy.get('input[name=username]').type(user.username)
				cy.get('input[name=password]').type(user.password)
				cy.get('form').submit()
				cy.get('h1').should('contain', user.username)
			})
		}
	})
})

然后执行测试观察结果即可

断言

Cypress的断言基于Chai断言库,并且增加了对Sinon-Chai、Chai-jQuery断言库的支持,并且Cypress支持多种风格的断言包括BDD风格(expect/should)和TDD风格(assert)格式的断言

针对长度的断言

//重试, 直到找到3个匹配的<li.selected>
cy.get('li.selected').should('hava.length',3)

针对类的断言

//重试,直到input元素没有类被disabled为止或直到超时为止
cy.get('form').find('input').should('not.hava.clacc', 'disabled')

针对值的断言

//重试,直到textarea的值为'davieyang'
cy.get('testarea').should('hava.value', 'davieyang')

针对文本内容的断言

//重试,知道这个span不包含“click me”字样
cy.get('a').parent('span.help').should('not.contain', 'click me')

针对元素是否可见的断言

//重试,直到这个button可见为止
cy.get('button').should('be.visible')

针对元素存在与否的断言

//重试,直到id为loading的空间不存在
cy.get('#loading').should('not.exist')

针对元素状态的断言

//重试,直到这个radio button是选中状态为止
cy.get(':radio').should('be.checked')

针对CSS的断言

//重试,直到completed的这个类有匹配的CSS为止
cy.get('.completed').should('hava.css', 'text-decoration', 'line-through')

针对回调函数的断言

假设源HTML如下

<div class="main-davieyang heading-yangdavie83">introduction</div>

如果判断类名是否一定含有heading字样,则

cy.get('div').should(($div)=>{
	expect($div).to.have.length(1)
	const className = $div[0].className
	//检查类名匹配通配符/heading-/
	expect(className).to.match(/heading-/)
	})

更详细的可参考chaisinon-chaichai-jquerychaijs-assert

Test Runner

在这里插入图片描述
Cypress TestRunner是众多测试框架中特殊的存在,它使得测试在一个独特而强大的交互式运行器中运行,在同一个窗口内不仅可以看到测试的实际执行,还能看到被测应用的状态变化,如图所示大致分为两大部分

  • 第一个区域中从左到右分别是

    • 测试运行结果和状态,包括测试用例执行的成功和失败条数以及每个测试用例运行的时间
    • Cypress元素定位辅助器(Selector Playground),就是蓝色圈出的按钮,点击该按钮便可以识别页面元素
    • URL预览区,展示的是测试并令执行时被测应用程序所处的URL,它可以更方便的查看测试路由
    • 视窗大小,可以通过在cypress.json中配置viewportWidth和viewportHeight两个配置项来控制视窗大小
  • 第二个区域分为两个部分,左侧是Command Log,也就是测试用例中各个命令执行的详细情况;右侧伴随着Command的执行应用程序的实时状态预览

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Davieyang.D.Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值