Mocha中文指南

mocha是一款比较流行的测试框架,出自TJ之手,跟jasmine相比,它有灵活的断言语法,测试提示也比较友好,其它方面跟jasmine类似.

使用条件

  • npm install -g mocha

  • mocha *.js

mochajasmine一个显著区别在于,它的断言库不是固定的,支持nodejs自带的assert库,should.js,expect.js,chai,better-assert,然后也支持BDD语法的describeit.

因为should.js也是TJ写的,所以这里重点介绍它的断言语法,更多它的API使用文档,可以点击这里

should.js

下面重点介绍它的一些常用手法,测试环境可以使用mocha

先说下,should.js的一个特点,就是它支持友好语法的链式调用,这些语法其实什么都没做,只是编写比较友好,方便阅读,常见的语法有下面这些

be,an,of,a,and,have,with,is,which,the,在should后面加上not代表相反的意思.

结合比较操作方法,看看下面一个简单的例子

require('should');

describe('test should.js', function(){
    it('test chain', function(){
        (10).should.is.be.the.eql(10);
    });
});

上面例子中的is,be,the其实没有顺序要求,读起来怎么友好就怎么写,因为它们什么都没做,只是返回should.js包装函数.

ok

检查期望的值能否转换成`true`
(true).should.be.ok;

true | True

检查期望的值是否为`true`,不转换
(true).should.be.true;
false.should.not.be.True;

false | False

检查期望的值是否为`false`,不转换
(true).should.not.be.false;
false.should.be.False;

eql

检查期望的值跟真实的值在字面上是否相等,并且深度比较
require('should');

describe('test should.js', function(){
    var foo = {
        foo: 'foo',
        bar: {
            bar: 'bar'
        }
    };
    it('test eql', function(){
        (foo).should.be.eql({
            foo: 'foo',
            bar: {
                bar: 'bar'
            }
        });
    });
});

equal | exactly

检查期望的值与真实的值是一样的,当是比较值类型的话,跟`eql`一样,比较引用类型的话,
则要检查两者是否引用同一地址.
require('should');

describe('test should.js', function(){
    var foo = {
        foo: 'foo',
        bar: {
            bar: 'bar'
        }
    };
    it('test eql', function(){
        (foo).should.not.be.equal({
            foo: 'foo',
            bar: {
                bar: 'bar'
            }
        });
    });
});

throw | throwError

检查期望的函数是否会返回异常,方法参数支持异常信息,正则表达式,对象,以便精确匹配错误信息
(function(){ throw new Error('fail') }).should.throw();
(function(){ throw new Error('fail') }).should.throw('fail');
(function(){ throw new Error('fail') }).should.throw(/fail/);

(function(){ throw new Error('fail') }).should.throw(Error);
var error = new Error();
error.a = 10;
(function(){ throw error; }).should.throw(Error, { a: 10 });
(function(){ throw error; }).should.throw({ a: 10 });

match

检查期望的值与传入的`正则`,`函数`,`对象`进行匹配

匹配规则如下:

  • 如果参数是正则并且期望值是字符串的话,则直接正则匹配字符串即可.
  • 如果参数是正则并且期望值是数组的话,则依次用正则匹配数组元素.
  • 如果参数是正则并且期望值是对象,则依次对对象键值用正则匹配.
  • 如果参数是函数,当函数抛异常或者返回false,则判定为没匹配上.
  • 如果参数是对象,则相同键值用上面的规则来匹配.
  • 其它情况都适为没匹配上.
'foobar'.should.match(/^foo/);
'foobar'.should.not.match(/^bar/);

({ a: 'foo', c: 'barfoo' }).should.match(/foo$/);

['a', 'b', 'c'].should.match(/[a-z]/);

(5).should.not.match(function(n) {
  return n < 0;
});
(5).should.not.match(function(it) {
   it.should.be.an.Array;
});
({ a: 10, b: 'abc', c: { d: 10 }, d: 0 }).should
.match({ a: 10, b: /c$/, c: function(it) {
   return it.should.have.property('d', 10);
}});

[10, 'abc', { d: 10 }, 0].should
.match({ '0': 10, '1': /c$/, '2': function(it) {
   return it.should.have.property('d', 10);
}});

matchEach

依次对期望值进行匹配

匹配规则如下:

  • 如果参数是正则,则依次对期望值中的对象值或者数组项进行正则匹配
  • 如果参数是函数,当函数抛异常或者返回false,则没匹配上
  • 其它情况,则直接按eql来处理
[ 'a', 'b', 'c'].should.matchEach(/\w+/);
[ 'a', 'a', 'a'].should.matchEach('a');

[ 'a', 'a', 'a'].should.matchEach(function(value) { value.should.be.eql('a') });

{ a: 'a', b: 'a', c: 'a' }.should.matchEach(function(value) { value.should.be.eql('a') });

Infinity

检查期望的值是否为无穷大或者无穷小
(10).should.not.be.Infinity;
NaN.should.not.be.Infinity;

NaN

检查期望的值是否为NaN
(10).should.not.be.NaN;
NaN.should.be.NaN;

above | greaterThan

检查期望的值是否大于某数
(10).should.be.above(0);
(10).should.not.be.greaterThan(100);

approximately

检查期望的值大约在(某个数±某个半径)内.
// 9.99 10±0.1
(9.99).should.be.approximately(10, 0.1);

below | lessThan

检查期望的值小于某数
(0).should.be.below(10);
(100).should.not.be.lessThan(10);

within

检查期望的值在某两个数之前,包含两个数
(10).should.be.within(10, 20);
(13).should.be.within(10, 15);

empty

检查期望的值是否为空,比如空字符串,空数组,空对象.
''.should.be.empty;
[].should.be.empty;
({}).should.be.empty;

enumerable

检查期望的值是否有可枚举的属性
({ a: 10 }).should.have.enumerable('a');

enumerables

检查期望的值是否有多个可枚举的属性
({ a: 10, b: 10 }).should.have.enumerables('a', 'b');
({ a: 10, b: 10 }).should.have.enumerables(['a', 'b']);

keys | key

检查期望的值是否包含传入的键
({ a: 10 }).should.have.keys('a');
({ a: 10, b: 20 }).should.have.keys('a', 'b');
({ a: 10, b: 20 }).should.have.keys([ 'a', 'b' ]);
({}).should.have.keys();

length | lengthOf

检查期望的值的长度
[1, 2].should.have.length(2);

ownProperty

检查期望的值是否有自己的某个属性
({ a: 10 }).should.have.ownProperty('a');

properties

检查期望的值是否有某些属性,可以批量检查属性
({ a: 10 }).should.have.properties('a');
({ a: 10, b: 20 }).should.have.properties([ 'a' ]);
({ a: 10, b: 20 }).should.have.properties({ b: 20 });

property

检查期望的值是否有某个属性,单个检查
({ a: 10 }).should.have.property('a');
({ a: 10 }).should.have.property('a', 10);

propertyByPath

根据传入的参数深度来获取属性的值
({ a: {b: 10}}).should.have.propertyByPath('a', 'b').eql(10);

propertyWithDescriptor

检查期望的值的属性有某些描述
({ a: 10 }).should.have.propertyWithDescriptor('a', { enumerable: true });

startWith

检查字符串是否以某个传入的参数开头
'abc'.should.startWith('a');

endWith

检查字符串是否以某个传入的参数结尾
'abc'.should.endWith('c');

instanceof | instanceOf

检查期望的值是否是某个类的实例
'abc'.should.be.instanceof(String);

type

检查期望的值是否是某个类型
'abc'.should.be.type('string');

null | Null

检查期望的值是否为null
var should = require('should');
describe('test should.js ', function(){
    it('test null ', function(){
        should(null).be.null;
    });
});

undefined | Undefined

检查期望的值是否undefined
var should = require('should');
describe('test should.js ', function(){
    it('test undefined', function(){
        var a;
        should(a).be.undefined;
    });
});

上面是一些常用的should.js中的断言方法,下面简单的说下mocha测试框架的用法

异步处理与前后注入

jasmine类似,也是通过在it,beforeEach里传入done来确定是否执行完成

describe('test should.js', function(){

    var foo = 0;

    before('首次调用',function(done){
        setTimeout(function(){
            foo += 1;
            done()
        }, 1000);
    });
    beforeEach(function(done){
        setTimeout(function(){
            foo += 1;
            done();
        }, 1000);
    })
    afterEach(function(){
        foo = 0;
    });
    it('test beforeEach', function(done){
        setTimeout(function(){
            (foo).should.eql(2);
            done();
        }, 1000);
    })


});

禁用与开启测试

  • .only可以让某个it执行而忽略别的it
describe('1.test mocha', function(){

    it.only('test .only', function(){
        '123'.should.be.String;
    });

    // 下面的spec不会执行
    it('ignore invoke', function(){
        (0).should.be.eql(0);
    });
});
  • .skip可以禁止某个it而执行别的it
describe('test mocha', function(){

    // 下面的spec不会执行
    it.skip('ignore spec', function(){
        ({}).should.be.Object;
    });

    it('test .skip', function(){
        ({}).should.be.empty;
    });

});

*切换bdd,tdd,exports模式

mocha -u [bdd | tdd | exports] *.js

-u选项支持传递要使用的测试类型,默认是bdd,可以选择tdd,exports

BDD

bdd用的比较多,jasmine的测试风格就是bdd,它的特征就是使用describe,it

describe('Array', function(){
  before(function(){
    // ...
  });

  describe('#indexOf()', function(){
    it('should return -1 when not present', function(){
      [1,2,3].indexOf(4).should.equal(-1);
    });
  });
});

TDD

tddbdd区别在于,它使用suite,test,suiteSetup,suiteTeardown,setup,teardown

suite('Array', function(){
      setup(function(){
            // ...
      });

      suite('#indexOf()', function(){
            test('should return -1 when not present', function(){
                ([1,2,3].indexOf(4)).should.be.eql(-1);
            });
      });
});

Exports

exports类似于node里的模块语法,before, after, beforeEach, and afterEach是作为对象的属性来处理,其它对象的值默认是suite,属性是函数的话,代表是一个test

module.exports = {
      before: function(){
            // ...
      },

      'Array': {
            '#indexOf()': {
              'should return -1 when not present': function(){
                [1,2,3].indexOf(4).should.equal(-1);
              }
            }
      }
};

其实的测试风格还有QUnit,Require,更多详情请点击这里.

*切换输出风格

目前默认的输出格式为spec,可以通用-R spec来指定,目前已有的格式有dot,nyan,tap,list,progress,json,min,doc,markdown等,html格式只在浏览器中有效

mocha细节问题

  • 设置测试组超时时间,可以在describe或者suite代码块里调用this.timeout方法
describe('a suite of tests', function(){
    this.timeout(500);

    it('should take less than 500ms', function(done){
        setTimeout(done, 300);
    })

    it('should take less than 500ms as well', function(done){
        setTimeout(done, 200);
    })
})
  • 设置测试用例超时时间,可以在it或者test代码块里调用this.timeout方法
it('should take less than 500ms', function(done){
  this.timeout(500);
  setTimeout(done, 300);
})
  • mocha默认会执行./test/*.js里的测试文件,所以这个目录是放测试文件的好地方
  • 为了方便执行mocha命令,可以建立一个makefile文件,如下
test:
    ./node_modules/.bin/mocha --reporter list

.PHONY: test

下次运行的话,直接在终端上输入make test

总结

mocha是一个功能比较丰富的测试框架,希望更多的前端工作者们能用上它.


上一篇:关于实现一个基于文件持久化的EventStore的核心构思


下一篇:关于MySQL的在线扩容