Mocha入坑指南

442 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

最近一段时间刚刚接手了一个angular1.3前端+node服务器端多项目,作为一个刚刚入门node的新手,在熟悉了node的代码后便对已有的接口进行了单元测试用例的编写,此篇博客用于简单的记录。

Mocha介绍

Mocha是非常流行JavaScript测试框架之一,在浏览器和Node环境都可以使用。

安装

npm i mocha -g

简单测试脚本

1.首先写一个简单的例子,add.js

function add(a, b){ 
    return a+b; 
} 
module.exports = add;

2.新建测试脚本 add.test.js,一般命名规则测试脚本和原脚本同名,但是后缀名为.test.js

let calcu = require('./add');
let should = require("should");

describe("add func test",() => {
    it('2 add 2 should equal 4',() => {
      calcu.add(2,2).should.equal(4)
    })
})

3.执行测试用例

mocha demo1/mocha demo1/calcu.test.js

describe 表示测试套件,是一序列相关程序的测试 it表示单元测试(unit test),也就是测试的最小单位。

断言库

断言库可以理解为比较函数,也就是断言函数是否和预期一致,如果一致则表示测试通过,如果不一致表示测试失败,一个unit test里面可以包含多个断言语句。 本身mocha是不包含断言库的,所以必须引入第三方断言库,目前比较受欢迎的断言库 有 should.js、expect.js 、chai,而chai包含should、expect和assert三种风格,可扩展性比较强。 这里举例说明should断言方法:

// 全等,相当于=== 
.exactly 
(5).should.be.exactly(5) 

// 对象存在 
.ok 
true.should.be.ok; 
'yay'.should.be.ok; 
(1).should.be.ok; 
({}).should.be.ok; 
false.should.not.be.ok; 

// 真 
.true 
(5===5).should.be.true 
(err === null).should.be.true; 

// 相等,相当于 == 
.eql 
({ foo: 'bar' }).should.eql({ foo: 'bar' }); 
[1,2,3].should.eql([1,2,3]); 
// see next example it is correct, even if it is different types, but actual content the same 
[1, 2, 3].should.eql({ '0': 1, '1': 2, '2': 3 }); 

// 非数字 
.NaN 
(undefined + 0).should.be.NaN; 

// 判断类型 
.typeof 
user.should.be.type('object'); 
'test'.should.be.type('string'); 

// 构造函数的一个实例 
.instanceof user.should.be.an.instanceof(User); 
[].should.be.an.instanceOf(Array); 

// 存在 
.exist() 
should.not.exist(err) 

//深度包含 
.containDeep() 
[[1],[2],[3]].should.containDeep([[3]]); 
[[1],[2],[3, 4]].should.containDeep([[3]]); 
[{a: 'a'}, {b: 'b', c: 'c'}].should.containDeep([{a: 'a'}]); 
[{a: 'a'}, {b: 'b', c: 'c'}].should.containDeep([{b: 'b'}]); 

// 抛出异常 
.throw()和throwError() 
(function(){ throw new Error('fail'); }).should.throw(); 
(function(){ throw new Error('fail'); }).should.throw('fail'); 

// http响应的头部包含 
.header 
res.should.have.header('content-length'); 
res.should.have.header('Content-Length', '123'); 

// 包含或等价于 
.containEql 
({ b: 10 }).should.containEql({ b: 10 }); 
([1, 2, { a: 10 }]).should.containEql({ a: 10 });

用法

常规函数测试

如上面的add.test.js

异步函数测试

新建文件book.js

let fs = require('fs');

exports.read = (cb) => {
        fs.readFile('./book.txt', 'utf-8', (err, result) => {
            if (err) return cb(err);
            console.log("result",result);
            cb(null, result);
        }) 
}

新建文件book.test.js

let book = require('./book');
let expect = require("chai").expect;
let book = require('./book');
let expect = require("chai").expect;

describe("async", () => {
  it('read book async', function (done) {
    book.read((err, result) => {
      expect(err).equal(null);
      expect(result).to.be.a('string');
      done();
    })
  })
})

运行mocha book.test.js,我们会发现成功了,但是如果我们把book.js增加一个定时函数,改为如下例子:

let fs = require('fs');
exports.read = (cb) => {
    setTimeout(function() {
        fs.readFile('./book.txt', 'utf-8', (err, result) => {
            if (err) return cb(err);
            console.log("result",result);
            cb(null, result);
        }) 
    }, 3000);
}

会发现报如下错误:

Timeout of 2000ms exceeded.

这是因为mocha默认每个测试用例最多执行2000毫秒,如果到时没有得到结果,就报错。所以我们在进行异步操作的时候,需要额外指定timeout时间。

mocha --timeout 5000 book.test.js

这样就保证测试用例成功。

API测试

单单使用Mocha和should就几乎可以满足所有JavaScript函数的单元测试。但是对于Node应用而言,不仅仅是函数的集合,比如一个web应用的测试。这时候就需要配合一个http代理,完成Http请求和路由的测试。 Supertest是一个HTTP代理服务引擎,可以模拟一切HTTP请求行为。Supertest可以搭配任意的应用框架,从而进行应用的单元测试。 1.安装supertest npm i supertest --save-dev 2.传入应用来实例化supertest

let app = new koa();
let server = app.listen(0);
this.request = supertest(server);

3.调用API进行测试

//get
describe('GET /users', function(){ 
    it('respond with json', function(done){ 
        request(app) 
        .get('/user') 
        .set('Accept', 'application/json') 
        .expect(200) 
        .end(function(err, res){ 
            should.not.exist(err); 
            res.text.should.containEql('success'); 
            done(); 
        }); 
    }); 
});
//post
describe('test login', function(){ 
    it('login sucessfully', function (done) { 
        request.post('/user') 
        .send({ username: 'username', password: '123456' }) 
        .end(function (err, res) { 
            should.not.exists(err); 
            done(); 
        }); 
    }); 
})

另外,可以通过.attach()方法测试文件上传。

常用命令行

node ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha
open coverage/lcov-report/index.html

生命钩子

mocha一共四个生命钩子

  • before():在该区块的所有测试用例之前执行
  • after():在该区块的所有测试用例之后执行
  • beforeEach():在每个单元测试前执行
  • afterEach():在每个单元测试后执行