写这篇文章的初衷是,有个朋友来找我写单元测试...本身懵逼了,开发了这么久,还真没有好好写过测试用例,而后就和我朋友掰扯,个人论点是,前端开发不必写单元测试,通常公司都有端对端测试就是点点点...开发人员一旦书写测试用例,无形增长人员的开销,或者是延缓迭代速率...他一句话,上头须要...两人哑口无言乘着空闲的时候就查阅了各类资料
思考一:是否有必要单元测试?(团队是否拥有测试团队的两种状况)
思考二:实在要编写单元测试如何编写优雅的测试用例模块?javascript你们有空不妨告诉我大家公司会用到单元测试么?有必要么?前端
下面先自我归总下
Mocha
单元测试的基础api使用和注意点java
字如其名,mocha(摩卡咖啡)其 官网文档也是如此通常
JavaScript 具备多种工具和框架,这些工具和框架能够在 Node.js 以及浏览器中进行测试驱动开发,例如:Jest, Jasmine, Mocha, QUnit, Karma, Cypress 等。
而mocha
就是测试驱动开发中众多工具之一
安装 所需依赖git
npm i mocha -g 全局安装 npm i mocha --save-dev 项目依赖中安装
终端中执行mocha
将在当前目录中须要test/
文件夹并执行对应的测试用用例文件
下面咱们创建个test目录并新建index.test.js
文件github
// index.test.js function add(v,v2){ return v + v2; } describe('# add()', function() { it('should return 3 when the value 1 、 2', function() { expect(add()).to.be.equal(3) }) })
以上一个测试add()
方法运算逻辑是否正常的功能测试用例就算是编写好了
可是真实项目中,咱们通常不会如此简单的业务逻辑;通常都会包含测试异步代码、测试同步代码,以及一些骚操做提升效率
npm
异步代码在现代开发中很常见,mocha针对异步代码也有对应的解决方案
1.使用回调方法
2.使用 Promise (用于支持 Promise 的环境)
3.使用async
/await
(用于支持异步 function 的环境)
异步接口请求的时候可使用 supertest提供http请求方案
//异步接口请求测试 it('异步请求应该返回一个对象', function(done){ supertest .get('https://api.github.com') .end(function(err, res){ expect(res).to.be.an('object'); done(); }); });
如上使用 supertest
方法能很好的进行异步回调处理api
var expect = require('chai').expect; function asyncFn (string, callback){ var withCallback \= typeof callback \=== 'function'; try { let hash = string; withCallback && callback(null, hash); } catch (e) { if (withCallback) { callback(e); } else { throw e; } } } describe('# asyncFn()', function() { it('异步函数回调处理', function(done) { asyncFn('test',function(err,str){ // 使用 error 来调用 done() 回调函数, // 来终止带有错误的测试 if(err) return done(err); expect(str).to.be.equal('test'); // 调用 done() 回调函数来终止测试 done(); }) }) })
如上使用回调函数处理异步代码promise
function promiseAsyncFn (str){ return new Promise((reolve,reject)=>{ asyncFn(string, (err, str) => { return err ? reject(err) : resolve(str); }) }) } describe('# asyncFn()', function() { it('异步函数回调处理 resolve', function(done) { return promiseAsyncFn('test').then(function()=>{ expect(str).to.be.equal('test'); done() }) }) it('异步函数回调处理 reject', function(done) { return promiseAsyncFn('test').catch( function(err)=>{ expect(function(){throw err}).to.throw(TypeError, 'The "data" argument must be one of type string, TypedArray, or DataView. Received type number'); done(err) }) }) })
describe('# asyncFn()', function() { it('异步函数回调处理 resolve', async function(done) { let data = await promiseAsyncFn('test') expect(data).to.be.equal('test'); done() }) it('异步函数回调处理 reject',async function(done) { await promiseAsyncFn('test').catch( function(err)=>{ expect(function(){throw err}).to.throw(TypeError, 'The "data" argument must be one of type string, TypedArray, or DataView. Received type number'); done(err) }) }) })
看完上面三种异步代码的处理方法熟悉js的小伙伴要开始扔砖了,和JavaScript好像同样,我写完以后发现确实同样,可是咱们不能大意其中done()的特性!浏览器
done()
使用的主意事项细心的同窗应该发现了每一个it用例汇总总会出现个expect
,这是配合mocha处理错误判断异常的断言库chai
模块中的一个方法,其余如 Expect.js, Should.js;mocha中也拥有内置的assert
模块可是咱们优先使用开发中更加经常使用的以及功能强大的chai
npm i --save-dev chai
经常使用的expect断言风格以下框架
// 相等或不相等 expect(4 + 5).to.be.equal(9); expect(4 + 5).to.be.not.equal(10); expect(foo).to.be.deep.equal({ bar: 'baz' }); // 布尔值为true expect('everthing').to.be.ok; expect(false).to.not.be.ok; // typeof expect('test').to.be.a('string'); expect({ foo: 'bar' }).to.be.an('object'); expect(foo).to.be.an.instanceof(Foo); // include expect([1,2,3]).to.include(2); expect('foobar').to.contain('foo'); expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); // empty expect([]).to.be.empty; expect('').to.be.empty; expect({}).to.be.empty; // match expect('foobar').to.match(/^foo/);
观察规律咱们不难发现:expect
断言的写法都是同样的。头部是expect
方法,尾部是断言方法,好比equal
、a
/an
、ok
、match
等。二者之间使用to
或to.be
链接。
var assert = require('chai').assert; var numbers = [1, 2, 3, 4, 5]; assert.isArray(numbers, 'is array of numbers'); assert.include(numbers, 2, 'array contains 2'); assert.lengthOf(numbers, 5, 'array contains 5 numbers');
var should = require('chai').should(); var numbers = [1, 2, 3, 4, 5]; numbers.should.be.an('array').that.includes(2); numbers.should.have.lengthOf(5);
Mocha 提供了建立测试 hooks 的功能,hooks 基本上是配置为在测试以前或以后运行的逻辑。它们对于设置测试的前提条件或者在测试后清理资源颇有用。使用默认的 BDD 接口,Mocha 提供了四个 hooks:
before()
- 在块中的第一个测试用例以前运行一次beforeEach()
- 在每一个测试用例前运行afterEach()
- 在每一个测试用例以后运行after()
- 在块中的最后一个测试用例以后运行一次执行顺序以下
before() -> beforeEach() -> test() -> afterEach() -> after()
建立一个简单的hooks
describe('hooks', function() { before(function() { // 在本区块的全部测试用例以前执行 }); after(function() { // 在本区块的全部测试用例以后执行 }); beforeEach(function() { // 在本区块的每一个测试用例以前执行 }); afterEach(function() { // 在本区块的每一个测试用例以后执行 }); // test cases });
mocha 同时具有一些自身可配参数,好比修改用例超时时间、递归执行test文件夹下的全部路径、使用
skip()、only()
控制it、describe是否执行...
1. 包含/排除测试skip()、only()
// 此测试套件中的测试将运行 describe.only('#flattenArray()', function() { describe('#二级-1()', function() { it.only('should merge two arrays', function() {}) it('should merge two arrays123123', function() {}) }) // it('should flatten array', function() {}) describe.skip('#二级-2()', function() { it('should merge two arrays', function() {}) }) }) // 此测试套件中的测试没法运行 describe('#mergeArray()', function() { it('should merge two arrays', function() {}) })
结果以下:
only会影响测试套件、测试用例
如上在第一个describe使用了only以后相邻的测试用例则被规避,不执行了
2. 重复测试
mocha 提供了 this.retries () 函数,该函数可让您指定容许重试的次数。对于每次重试,Mocha 都会运行从新 beforeEach () 和 afterEach () hooks,但不会从新运行 before () 和 after () 钩子。
// this.retries() 函数的用法 describe('test medium site', function() { // 这个套件中全部失败的测试都会重试2次 this.retries(2) it('should load medium homepage', function() { // 这个测试用例测试失败后,会被重复5次 this.retries(5) cy.visit('https://medium.com') }) })
3. 慢速定义this.slow(value)
设定一个值界定当前测试用例超过这个临界点是慢的
describe('slow test', function() { // 在一秒以后,测试将被认为是缓慢的 this.slow(1000) // 在指定的一秒钟以后完成 it('should be complete in a second', function(done) { setTimeout(done, 1500) }) // 当即完成 it('should be complete instantly', function() {}) })
4. 超时定义this.timeout(value)
可设置一个数值告诉mocha最大等待时间,超出时间则超时,mocha默认超时时间是2S
describe('some time-consuming operation', function() { // 给这个测试用例设置 5 秒的超时时间 this.timeout(5000) before('some long step', function(done) { // 设置 hooks 的超时时间 this.timeout(2500) setTimeout(done, 2250) }) it('should take less than 200ms', function(done) { // 为当前测试用例设置超时时间 this.timeout(200) setTimeout(done, 150) }) })
mocha --watch // 标志指示 Mocha 监听测试文件的变更并从新运行测试。这对于在开发过程当中编写测试很是有用。 mocha --async-only // 标志强制全部测试必须传递回调函数或返回一个 promise,从而异步执行。对于未指定回调函数或者未返回 promise 的测试将被标记为失败 mocha --bail // 标志指定只要有一个测试用例没有经过,就中止后面全部的测试用例。这对于持续继承颇有用 mocha -t 3000 // 等价于`this.timeout()`方法,这个做用于全局 mocha --no-timeouts // 等价于`--timeout 0``this.timeout(0)`,表示~~~~禁用超时限制 mocha --slow 100 // 默认的阈值是 75ms,标记速率的阈值 mocha --require should // 标志容许您导入在测试文件中使用的模块 / 库(例如断言库),而不是在代码中手动调用 require ()。 mocha -R list // 能够指定测试报告的格式。默认的是`spec`格式。Mocha 还容许您使用此标志指定第三方报告格式。
mocha 还能使用将测试报告跑在浏览器中,结合一些插件能提升必定效率;
说到这里,我认为一个成熟的开发团队,应该是有必要也是必须有意识的书写测试用例,为后人以及新需求搭建稳固的基础,啦啦啦
欢迎来到本身的blog小站留言