mocha介绍 #

mocha 是一个功能丰富的javascript测试框架,可以运行在nodejs和浏览器环境,使异步测试变得简单有趣。mocha 串联运行测试,允许灵活和精确地报告结果,同时映射未捕获的异常用来纠正测试用例。 支持TDD/BDD 的 开发方式,结合 should.js/expect/chai/better-assert 断言库,能轻松构建各种风格的测试用例。

安装 #

通过npm全局安装: npm install -g mocha

最简单的测试用例 #

请在项目中新建一个test(注意:此目录名是定死的,mocha命令行工作会默认找此目录)目录,然后在里面新建一个array-includes.js文件

//测试用例首先需要引用断言模块
var assert = require("assert");
describe('includes', function () {
  it('数组中包含元素应该返回true', function () {
    //1. assert.equal(actual, expected, [message]) 语法。作用等同于使用'=='进行相等判断
    //2. actual为实际值,expected 为期望值。message为返回的信息
    assert.equal(true, [1, 2, 3].includes(2));
  });
  it('数组中不包含元素应该返回false', function () {
    assert.equal(false, [1, 2, 3].includes(5));
  });
});

运行mocha

$ mocha

断言 #

断言(assert)指的是对代码行为的预期。一个测试用例内部,包含一个或多个断言(assert)。 断言会返回一个布尔值,表示代码行为是否符合预期。测试用例之中,只要有一个断言为false,这个测试用例就会失败,只有所有断言都为true,测试用例才会通过。 mocha 允许开发者使用任意的断言库,当这些断言库抛出了一个错误异常时,mocha将会捕获并进行相应处理。这意味着你可以利用如 should.js断言库、 Node.js 常规的 assert 模块或其它类似的断言代码库。 以下是适用于Node.js或浏览器的断言库

断言库 #

Chai 是一个非常灵活的断言库,它可以让你使用如下三种主要断言方式的任何一种:

assert: #

这是来自老派测试驱动开发的经典的assert方式。比如:

assert.equal(variable, "value");

expect: #

这种链式的断言方式在行为驱动开发中最为常见。比如:

expect(variable).to.equal("value");

should: #

这也是在测试驱动开发中比较常用的方式之一。举例:

variable.should.equal("value");

expect 的语法 #

expect 库应用是非常广泛的,它拥有很好的链式结构和仿自然语言的方法。通常写同一个断言会有几个方法,比如expect(response).to.be(true) 和 expect(response).equal(true)。以下列举了 expect 常用的主要方法:

在课程开始讲解什么是mocha的时说:mocha支持TDD/BDD 的 开发方式,结合 should.js、expect、chai、better-assert 断言库,能轻松构建各种风格的测试用例。这里面有两个知识点,一个是断言库,另一个是 TDD/BDD 。 更多的文档可以看 http://chaijs.com/api/bdd/

BDD风格 #

mocha “接口” 系统允许开发者选择自身喜爱的特定领域语言风格, mocha 提供 TDD(测试驱动开发)、BDD (行为驱动开发) 和 exports 风格的接口。

BDD是“行为驱动的开发”(Behavior-Driven Development)的简称,指的是写出优秀测试的最佳实践的总称。

BDD认为,不应该针对代码的实现细节写测试,而是要针对行为写测试。BDD测试的是行为,即软件应该怎样运行。

BDD接口提供以下方法:

BDD的特征就是使用describe()和it() 这两个方法 before()、after()、beforeEach()和afterEach() 是为测试做辅助的作用域,它们合起来组成了hook的概念。

describe()和it() #

describe()方法接收两个参数:第一个参数是一个字符串,表示测试套件的名字或标题,表示将要测试什么。第二个参数是一个函数,用来实现这个测试套件。

上述中引出了一个概念:测试套件。那什么是测试套件呢?

测试套件(test suite)指的是,一组针对软件规格的某个方面的测试用例。也可以看作,对软件的某个方面的描述(describe)。结构如下:

describe("A suite", function() {
  // ...
});

it() #

要想理解it(),首先我们要知道什么是测试用例? 测试用例(test case)指的是,针对软件一个功能点的测试,是软件测试的最基本单位。一组相关的测试用例,构成一个测试套件。

测试用例由it函数构成,它与describe函数一样,接受两个参数:第一个参数是字符串,表示测试用例的标题;第二个参数是函数,用来实现这个测试用例。

BDD风格用例 #

var expect = require('chai').expect;

describe('Array', function(){
  before(function(){
    console.log('在测试之前运行');
  });

  describe('#indexOf()', function(){
    it('当值不存在时应该返回 -1', function(){
      expect([1,2,3].indexOf(4)).to.equal(-1);
    });
  });
});

TDD风格 #

TDD(测试驱动开发)组织方式是使用测试集(suite)和测试(test)。

每个测试集都有 setupteardown 函数。这些方法会在测试集中的测试执行前执行,它们的作用是为了避免代码重复以及最大限度使得测试之间相互独立。

TDD接口:

suite('Array', function(){ setup(function(){ console.log('测试执行前执行'); });

suite('#indexOf()', function(){ test('当值不存在时应该返回 -1', function(){ assert.equal(-1, [1,2,3].indexOf(4)); }); }); });

运行mocha:

mocha --ui tdd .js (表示的是文件名)

> mocha 默认是使用 bdd 的接口,所以在这里我们告诉mocha我们用的是tdd

## exports
exports类似于nodejs里的模块语法,关键字 before, after, beforeEach, 和 afterEach 是特殊保留的,值为对象时是一个测试套件,为函数时则是一个测试用例。

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

'Array': { '#indexOf()': { '当值不存在时应该返回 -1': function(){ expect([1,2,3].indexOf(4)).to.equal(-1); } } } };

运行 mocha

mocha --ui exports *.js

## hook 机制
hook 就是在测试流程的不同时段触发,比如在整个测试流程之前,或在每个独立测试之前等。

hook也可以理解为是一些逻辑,通常表现为一个函数或者一些声明,当特定的事件触发时 hook 才执行。

提供方法有:before()、beforeEach() after() 和 afterEach()。
- before():所有测试用例的统一前置动作
- after():所有测试用例的统一后置动作
- beforeEach():每个测试用例的前置动作
- afterEach():每个测试用例的后置动作

用法

describe('hooks', function() { before(function() { //在执行本区块的所有测试之前执行 });

after(function() { //在执行本区块的所有测试之后执行 });

beforeEach(function() { //在执行本区块的每个测试之前都执行 });

afterEach(function() { //在执行本区块的每个测试之后都执行 });

//测试用例

});


## 描述 hook
所有的 hook 都可以加上描述,这样可以更好地定位到测试用例中的错误。如果 hook 函数指定了名称,会在没有描述时使用函数名,例如:

beforeEach(function() { //beforeEach hook });

beforeEach(function needFun() { //beforeEach: namedFun });

beforeEach('some description', function() { //beforeEach:some description });


## 测试占位
测试用例占位只要添加一个没有回调的 it() 方法即可:

var assert = require('assert'); describe('Array', function() { describe('#indexOf()', function() { //同步测试 it('当值不存在时应该返回 -1', function() { assert.equal(-1, [1,2,3].indexOf(5)); assert.equal(-1, [1,2,3].indexOf(0)); }); });

describe('Array', function() { describe('#indexOf()', function() { //下面是一个挂起的测试 it('当值不存在时应该返回 -1'); }); }); });


## 仅执行指定测试
仅执行指定测试的特性可以让你通过添加 .only() 来指定唯一要执行的测试套件或测试用例:

describe('Array', function(){ describe.only('#indexOf()', function(){ ... }) })

或一个指定的测试用例:

describe('Array', function(){ describe('#indexOf()', function(){ it.only('当值不存在时应该返回 -1', function(){

})

it('当值不存在时应该返回 -1', function(){

})

}) })

> 注意只能出现一个 .only()

## 忽略指定测试
该特性和 .only() 非常相似,通过添加 .skip() 你可以告诉 Mocha 忽略的测试套件或者测试用例(可以有多个)。该操作使得这些操作处于挂起的状态,这比使用注释来的要好,因为你可能会忘记把注释给取消掉。

describe('Array', function(){ describe.skip('#indexOf()', function(){ ... }) })


或一个指定的测试用例:

describe('Array', function(){ describe('#indexOf()', function(){ it.skip('当值不存在时应该返回 -1', function(){

})

it('当值不存在时应该返回 -1', function(){

})

}) })


## 动态生成测试
由于mocha 可以使用 function.prototype.call 和function 表达式定义测试套件和测试用例,所以可以动态生成测试用例。

var assert = require('assert');

function add() { return Array.prototype.slice.call(arguments).reduce(function(prev, curr) { return prev + curr; }, 0); }

describe('add()', function() { var tests = [ {args: [1, 2], expected: 3}, {args: [1, 2, 3], expected: 6}, {args: [1, 2, 3, 4], expected: 10} ];

tests.forEach(function(test) { it('correctly adds ' + test.args.length + ' args', function() { var res = add.apply(null, test.args); assert.equal(res, test.expected); }); }); });



## 辅助模块
我们进行单元测试,一般都需要组合几个工具来来使用的。下面我们开始介绍:

chai断言库
chai 断言库支持BDD 的 expect/should 语法风格 和TDD的 assert 语法风格。(在第一章的中有讲到)

superagent
在用Node做Web开发的时候,模拟HTTP请求时必不可少的。这也就引出了superagent这个模块,它是一个模拟http请求的库。它作用是简化发起请求的库。

项目的package.json 代码如下:

{ "name": "mocha-test", "version": "0.0.1", "private": true, "scripts": { "start": "node app.js", "test": "mocha test" }, "devDependencies": { "superagent": "1.4.0", "chai": "3.4.0" } } ```