内容简介:学习单元测试的时候接触了很多概念karma、mocha、Jesmine、chai、expect、assert、should、sinon等,容易混乱,在此做个梳理。入门文章参考:Mocha是一个常用的JS测试框架,可以在浏览器和Nodejs环境使用。Mocha不带断言需要和断言库结合使用。项目中使用的也是Mocha+chai+sinon的结合。例如给ndfront组件写的单元测试,详情查看github仓库:
学习单元测试的时候接触了很多概念karma、mocha、Jesmine、chai、expect、assert、should、sinon等,容易混乱,在此做个梳理。
1. 测试框架 Mocha、Jesmine
1.1 Mocha
入门文章参考: www.ruanyifeng.com/blog/2015/1…
Mocha是一个常用的JS测试框架,可以在浏览器和Nodejs环境使用。Mocha不带断言需要和断言库结合使用。项目中使用的也是Mocha+chai+sinon的结合。例如给ndfront组件写的单元测试,详情查看github仓库: github.com/baihexx/ndf…
特点:灵活,可扩展性好,可配合不同的断言库使用,但是自身集成度不高
1.2 Jesmine
Jesmine也是常用的测试框架,项目中没有用这个。
特点:内置断言库,集成度高,方便支持异步测试,但是灵活性差,断言风格单一
2. 断言库
2.1 assert
eg:
var assert = requier('assert') describe('desc1', function() { it('desc2', function() { assert(a === 1, '预期a的值是1') }) }) 复制代码
2.2 should.js
should.js是个第三方断言库,常和Mocha联合使用。
-
使用方法1:
requier('should'): 扩展Object.prototype,增加should属性,所有Object可以直接获取should使用,eg:
var should = require('should'); (5).should.be.exactly(5).and.be.a.Number(); var a = null a.should.not.be.ok() // 报错 复制代码
-
使用方法2:
若是undefined或者null,并没有继承Object的原型链,没有should属性可用,可采用如下方法:
var should = require('should/as-function'); var a = null should(a).not.be.ok() // pass should(10).be.exactly(5).and.be.a.Number(); 复制代码
2.3 Chai
官网API: 安装方法等查看官网
Chai是个断言库,常和Mocha结合使用。他有多种断言风格(assertion style):assert, expect, should
- assert断言风格:和nodejs的assert模块类似(多了写语法糖),是一种非链式语言风格 eg:
var assert = requier('chai').assert assert.notEqual(3, 4, 'these numbers are not equal') 复制代码
- expect断言风格:expect和should都是BDD风格,是一种链式语言风格, 连接词有 to,be,been,is等自然语言, eg:
var expect = requier('Chai').expect expect([1, 2, 3]).to.be.an('array').that.includes(2) 复制代码
- should断言风格:should()扩展了Object.prototype,增加了should属性,使用方法如下。eg:
var should = require('chai').should() //actually call the function var foo = 'bar' foo.should.be.a('string') foo.should.equal('bar') foo.should.have.lengthOf(3) 复制代码
(注意should对IE兼容性不好)
3. 测试运行工具:karma
定义:A simple tool that allows you to execute JavaScript code in multiple real browsers. The main purpose of Karma is to make your test-driven development easy, fast, and fun.
Karma不是测试框架,也不是断言库,他会开启一个HTTP服务,将测试文件生成一个Html文件,在浏览器内运行、调试。Karma不指定测试框架,通过插件和Mocha、Jesmine、QUnit都可以结合使用。
配置项较多,根据官网说明配置,并不难。
测试覆盖率 :根据提示安装、配置即可生成覆盖率报告
4. 测试辅助工具:Sinon
为什么需要Sinon?在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,记录Log, 文件系统之类的),而我们没法控制这些外部依赖的对象。例如:前端项目通常是用Ajax去服务端请求数据,得到数据之后做进一步的处理。但是做单元测试的时候通常不真的去服务端请求数据,不仅麻烦,可能服务端接口还没做好,这种不确定的依赖使得测试变得复杂。所以我们需要模拟这个请求数据的过程,Sinon用来解决这个问题。
Sinon的工作本质是“测试替身”,测试替身用来替换测试中的部分代码,使得测试复杂代码变得简单。
Sinon提供了三个功能:示例讲解看入门文章,不再赘述
- spy(间谍):提供函数调用的信息,但不会改变函数的行为
- stub:与spies类似,但是会完全替换目标函数。这使得一个被stubbed的函数可以做任何你想要的 —— 例如抛出一个异常,返回某个特定值等等。
- mock:通过组合spies和stubs,使替换一个完整对象更容易
eg:admin/misc/user.js: user.getUser() (看不懂的随便看看,这是实际的项目代码单元测试)
user.getUser函数用来获取用户信息,用户输入工号后向服务端请求数据,我们的测试用例重点在前端代码,不应依赖服务端才可测试,so 应该模拟ajax请求。
(1)nd-spa中ajax.js请求代码如下:实际的请求函数为:request.get, so应该mock request的get函数
(2)测试用例代码如下:
- sinon.stub(obj, functionname, mockFun)
- ajax.js中的请求代码调用的set(),send(),end()函数实际是mock中定义的函数,并没有做实际的后端请求,end的callback直接返回数据
- 注意L11:设置函数最大时长,写成function(done)形式才可用this.timeout,否则es6的箭头函数中this是window
5. 多步骤测试用例(实际项目记录,可不看,估计看不明白)
用户在前端页面中通常是通过点击鼠标期待某种效果,这个过程通常不做单元测试,因为复杂度较高,且页面变动较快,性价比很低。但是项目中某些公共的业务组件,需求变动小,步骤相对简单,但使用又非常多,例如social管理后台中的搜索用户admin/misc/user.js:user.autoComplete(),完整的过程是:用户输入用户信息,然后选择搜索到的匹配用户,再点击搜索到的用户,该输入框的值变为选择的用户。这个过程如何编写单元测试。
这里涉及到多步骤的模拟。
(1)util.js 封装多步执行函数
// utils.js export function triggerHTMLEvents (target, event, process) { const e = document.createEvent('HTMLEvents') e.initEvent(event, true, true) if (process) process(e) target.dispatchEvent(e) return e } export function triggerMouseEvents (target, event, process) { const e = document.createEvent('MouseEvents') e.initEvent(event, true, true) if (process) process(e) target.dispatchEvent(e) return e } export function triggerUIEvents (target, event, process) { const e = document.createEvent('UIEvents') e.initEvent(event, true, true) if (process) process(e) target.dispatchEvent(e) return e } // timeout: 执行间隔可设置 function createStep ({ step, timeout }) { return new Promise((resolve, reject) => { setTimeout(() => { try { resolve(step()) } catch (err) { reject(err) } }, timeout || 0) }) } // 多步骤执行函数, 执行步骤封装在arr数组中 export function runSteps (arr) { if (arr.length === 0) { return } let firstStep = createStep(arr[0]) const others = arr.splice(1) others.forEach(item => { firstStep = firstStep.then(() => { return createStep(item) }) }) } 复制代码
(2)使用
L116:给input设置值
L117:触发input的change事件,执行nd-autocomplete/src/input.js中change事件,如何调用的看autocomplete组件 L122:点击搜索列表的item,将值设置到input,然后验证input的值
过程中遇到一个额外的问题,在此记录下,备忘
(1)user中getUsers函数用到ucOrgId,查看代码(var ucOrgId = auth.getAuth('uc_org_id') 且仅仅在登录代码中有auth.setAuth())可知该组织id信息必须有,但是测试页面中没有登录,获取不到该信息,so,造登录数据,并设置到auth中。
(2)造登录数据并设置到auth中时遇到一个问题:若在user.spec.js中 引入auth,then auth.setAuth(...), 结果不对
原因:user.js中在开头就执行了获取ucOrgId的函数,我们在测试代码中先引入auth和user,这时ucOrgId已经获取了,且是空值,即使假造登录数据的函数写在import user之前也是没用,因为es6有提升的功能,总是先执行import,导致函数执行在后面,解决方法是:将设置登录数据的函数写在单独的文件中,使用import的方法,且在user之前inport,就会达到先执行设置登录数据的效果。(具体看代码更清晰)
(import的提升再复习下)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 照片整理系列二 —— 照片整理及归档的辛酸历程
- 我自己整理的码农周刊一周分享整理
- 学习 Node.js,第 9 单元:单元测试
- Vue 应用单元测试的策略与实践 02 - 单元测试基础
- Vue 应用单元测试的策略与实践 04 - Vuex 单元测试
- Vue 应用单元测试的策略与实践 03 - Vue 组件单元测试
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Big Red Fez
Seth Godin / Free Press / 2002-01-15 / USD 11.00
YOUR WEB SITE IS COSTING YOU MONEY. IT'S ALSO FILLED WITH SIMPLE MISTAKES THAT TURN OFF VISITORS BEFORE THEY HAVE A CHANCE TO BECOME CUSTOMERS. According to marketing guru Seth Godin, a web s......一起来看看 《The Big Red Fez》 这本书的介绍吧!