学习Node API单元测试

77 阅读4分钟

学习Node API单元测试

Mocha

Mocha是一款运行在nodejs上的测试框架,支持同步和异步测试,同时还支持TDD,BDD等多种测试风格。

安装

npm install -g mocha

核心函数 describe it

mocha主要提供两个核心函数 describe it来进行测试用例的编写。describe函数我们称之为测试套件,它的核心功能是来描述测试的流程,it函数我们称之为一个测试单元,它的功能是来执行具体的测试用例。

describe('测试单元', () => {
    it('测试内容:单元1', () => {
      assert.equal(getTag(undefined), '[object Undefined]');
    });
 });

钩子函数

  • before:在执行测试套件之前触发该钩子;
  • after:在测试套件执行结束之后触发该钩子;
  • beforeEach:在每个测试单元执行之前触发该钩子;
  • afterEach:在每个测试单元执行结束后触发该钩子;

钩子的使用场景更多是在实际的业务场景中进行mock数据、测试数据收集、测试报告的自定义等;因此钩子也是mocha的核心功能之一

注意

  1. beforeEach会对当前describe下的所有子case生效。
  2. before和after的代码没有特殊顺序要求。
  3. 同一个describe下可以有多个before,执行顺序与代码顺序相同。
  4. 同一个describe下的执行顺序为before, beforeEach, afterEach, after
  5. 当一个it有多个before的时候,执行顺序从最外围的describe的before开始,其余同理。

断言库chai

安装

npm install --save-dev chai

expect 语法

// 引用 expect(期望得到的结果)
var expect = require('chai').expect;
expect(addNum(1,2)).to.be.equal(3); // 期望两数相加结果为3

// equal 相等或不相等
expect(4 + 5).to.be.equal(9);
expect(4 + 5).to.be.not.equal(10);

// above 断言目标的值大于某个value,如果前面有length的链式标记,则可以用来判断数组长度或者字符串长度
expect(10).to.be.above(5);
expect('foo').to.have.length.above(2);
expect([ 1, 2, 3 ]).to.have.length.above(2);

// 判断目标是否为布尔值true(隐式转换)
expect('everthing').to.be.ok;
expect(1).to.be.ok;
expect(false).to.not.be.ok;
expect(undefined).to.not.be.ok;
expect(null).to.not.be.ok;

// true/false 断言目标是否为true或false
expect(true).to.be.true;
expect(1).to.not.be.true;
expect(false).to.be.false;
expect(0).to.not.be.false;

// null/undefined 断言目标是否为null/undefined
expect(null).to.be.null;
expect(undefined).not.to.be.null;
expect(undefined).to.be.undefined;
expect(null).to.not.be.undefined;

// NaN 断言目标值不是数值
expect('foo').to.be.NaN;
expect(4).not.to.be.NaN;

// 判断类型大法
expect({ foo: 'bar' }).to.be.an('object');

// 包含关系:用来断言字符串包含和数组包含。如果用在链式调用中,可以用来测试对象是否包含某key 可以混着用
expect([1,2,3]).to.include(2);
expect('foobar').to.contain('foo');
expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');

// 判断空值
expect([]).to.be.empty;
expect('').to.be.empty;
expect({}).to.be.empty;

// match
expect('foobar').to.match(/^foo/);

// exist 断言目标既不是null也不是undefined 
var foo = 'hi' , bar = null, baz;**
expect(foo).to.exist;
expect(bar).to.not.exist;
expect(baz).to.not.exist;

// within断言目标值在某个区间范围内,可以与length连用
expect(7).to.be.within(5,10);
expect('foo').to.have.length.within(2,4);
expect([ 1, 2, 3 ]).to.have.length.within(2,4);

// instanceOf 断言目标是某个构造器产生的事例
var Tea = function (name) { this.name = name; } , Chai = new Tea('chai');
expect(Chai).to.be.an.instanceof(Tea);
expect([ 1, 2, 3 ]).to.be.instanceof(Array);

// property(name, [value]) 断言目标有以name为key的属性
// 并且可以指定value断言属性值是严格相等的,此[value]参数为可选
// 如果使用deep链式调用,可以在name中指定对象或数组的引用表示方法 
var obj = { foo: 'bar' };
expect(obj).to.have.property('foo');
expect(obj).to.have.property('foo', 'bar');// 类似于expect(obj).to.contains.keys('foo')

Sinon

安装

npm install sinon

引用

var sinon = require('sinon')

sinon.stub()

stub是用于模拟一个组件或模块的函数或程序。在测试用例中,简单的说,你可以用stub去模拟一个方法,从而避免调用真实的方法,使用stub你还可以返回虚构的结果。你可以配合断言使用stub。

stub可以胜任许多任务,例如:
  • 替换像ajax或其它外部函数等让测试变复杂或慢的调用
  • 根据函数的响应来触发不同的代码流程
  • 测试不寻常的条件,如抛出异常
举个例子

1.模拟读取文件

let fs = require('fs')
let writeFileStub = sinon.stub(fs,'writeFile',function(path,data,cb))

2.模拟外部请求

let axios = require('axios')
let axiosStub = sinon.stub(axios, 'request'); 

// 模拟请求成功
axiosStub.resolves({ data : '请求成功' });

// 模拟请求抛出异常
axiosStub.resolves({ error : '请求失败' });

// Stubs 也有一个 `callCount` 告诉你多少次被调用了多少次
expect(axiosStub.callCount).to.equal(1);

// 测试完,销毁
axiosStub.restore();

3.模拟数据库

it('should pass object with correct values to save', function() {
  var save = sinon.stub(Database, 'save');
  var info = { name: 'test' };
  var expectedUser = {
    name: info.name,
    nameLowercase: info.name.toLowerCase()
  };

  setupNewUser(info, function() { });

  save.restore();
  sinon.assert.calledWith(save, expectedUser);
});