NodeJs测试框架Mocha的安装与使用


Posted in NodeJs onMarch 28, 2017

Mocha是运行在nodejs和浏览器下的JavaScript的单元测试框架,官方文档在https://mochajs.org/,相当的容易上手和好用,单元测试框架其实都差不多,基本都包含下面内容:

用于写测试用例的宏,属性或者函数
断定库, 用于测试是否可以通过
辅助库,如hook库(测试前后调用某些函数或者方法),异常检查(某些函数在某些参数的情况下抛出异常), 输入组合(支持多排列的参数输入组合)等。
支持IDE的集成
下面就按照官方文档的顺序来简明扼要的

安装与初步的使用

在控制台窗口中执行下列命令:

$ npm install -g mocha
$ mkdir test
$ $EDITOR test/test.js

可以写如下代码:

var assert = require('assert');
describe('Array', function() {
 describe('#indexOf()', function () {
  it('should return -1 when the value is not present', function () {
   assert.equal(-1, [1,2,3].indexOf(5));
   assert.equal(-1, [1,2,3].indexOf(0));
  });
 });
});

回到控制台:

$ mocha

 .

 ✔ 1 test complete (1ms)

这里mocha会查找当前文件目录下test文件夹下的内容,自动执行。

断定库

这个是判定测试用例是否通过,默认下可以用nodejs的assert库,与此同时,Mocha支持我们使用不同的断定库,现在可以支持下面的断定库,每个断定库的用法有一些差异,自己可以参考相应的文档。

1 should.js(https://github.com/shouldjs/should.js) BDD style shown throughout these docs (BDD模式,本文档用的都是这个断定库)
2 better-assert(https://github.com/tj/better-assert) c-style self-documenting assert()(C-模型下的断定库)
3 expect.js (https://github.com/Automattic/expect.js)expect() style assertions (expect模式的断定库)
4 unexpected(http://unexpected.js.org/) the extensible BDD assertion toolkit
5 chai(https://github.com/chaijs) expect(), assert() and should style assertions

同步代码

同步代码表示测试的是同步函数,上面的Array相关的例子代码就是。这个比较好理解。

异步代码

只所以有异步代码测试,原因是在nodejs上许多异步函数,如下面的代码中,只有done()函数执行完毕后,该测试用例才算完成

describe('User', function() {
 describe('#save()', function() {
  it('should save without error', function(done) {
   var user = new User('Luna');
   user.saveAsync(function(err) {
    if (err) throw err;
    done(); // 只有执行完此函数后,该测试用例算是完成。
   });
  });
 });
});

详解describe和it

上面的实例代码比较简单,那么什么是describe和it呢? 大致上,我们可以看出describe应该是声明了一个TestSuit(测试集合) ,而且测试集合可以嵌套管理,而it声明定义了一个具体的测试用例。 以bdd interface为例,具体的源代码如下:

/**
   * Describe a "suite" with the given `title`
   * and callback `fn` containing nested suites
   * and/or tests.
   */
  context.describe = context.context = function(title, fn) {
   var suite = Suite.create(suites[0], title);
   suite.file = file;
   suites.unshift(suite);
   fn.call(suite);
   suites.shift();
   return suite;
  };

   /**
   * Describe a specification or test-case
   * with the given `title` and callback `fn`
   * acting as a thunk.
   */

  context.it = context.specify = function(title, fn) {
   var suite = suites[0];
   if (suite.pending) {
    fn = null;
   }
   var test = new Test(title, fn);
   test.file = file;
   suite.addTest(test);
   return test;
  };

Hooks(钩子)

实际上这个在写unit test是很常见的功能,就是在执行测试用例,测试用例集合前或者后需要某个回调函数(钩子)。Mocha提供了before(),after(), beforeEach() 和aftetEach(),示例代码如下:

describe('hooks', function() {
 before(function() {
  // runs before all tests in this block
  // 在执行所有的测试用例前 函数会被调用一次
 });

 after(function() {
  // runs after all tests in this block
  // 在执行完所有的测试用例后 函数会被调用一次
 });

 beforeEach(function() {
  // runs before each test in this block
   // 在执行每个测试用例前 函数会被调用一次
 });

 afterEach(function() {
  // runs after each test in this block
  // 在执行每个测试用例后 函数会被调用一次
 });

 // test cases
});

hooks还有下列其他用法:

Describing Hooks - 可以对钩子函数添加描述,能更好的查看问题
Asynchronous Hooks (异步钩子): 钩子函数可以是同步,也可以是异步的,和测试用例一下,下面是异步钩子的示例代码:

beforeEach(function(done) {
  // 异步函数
  db.clear(function(err) {
   if (err) return done(err);
   db.save([tobi, loki, jane], done);
  });
 });

Root-Level Hooks (全局钩子) - 就是在describe外(测试用例集合外)执行,这个一般是在所有的测试用例前或者后执行。
Pending Tests (挂起测试)

就是有一些测试,现在还没有完成,有点类似TODO, 如下面的代码:

describe('Array', function() {
 describe('#indexOf()', function() {
  // pending test below 暂时不写回调函数
  it('should return -1 when the value is not present');
 });
});

Exclusive Tests (排它测试)

排它测试就是允许一个测试集合或者测试用例,只有一个被执行,其他都被跳过。如下面测试用例集合:

describe('Array', function() {
 describe.only('#indexOf()', function() {
  // ...
 });
   // 测试集合不会被执行
  describe('#ingored()', function() {
  // ...
 });
});

下面是对于测试用例:

describe('Array', function() {
 describe('#indexOf()', function() {
  it.only('should return -1 unless present', function() {
   // ...
  });
   // 测试用例不会执行
  it('should return the index when present', function() {
   // ...
  });
 });
});

需要说明的是,对于Hooks(回调函数)会被执行。

Inclusive Tests(包含测试)

与only函数相反,skip函数,将会让mocha系统无视当前的测试用例集合或者测试用例,所有被skip的测试用例将被报告为Pending。
下面是对与测试用例集合的示例代码:

describe('Array', function() {
  //该测试用例会被ingore掉 
 describe.skip('#indexOf()', function() {
  // ...
 });
  // 该测试会被执行
  describe('#indexOf()', function() {
  // ...
 });
});

下面例子是对具体的测试用例:

describe('Array', function() {
 describe('#indexOf()', function() {
   // 测试用例会被ingore掉
  it.skip('should return -1 unless present', function() {
   // ...
  });
   // 测试用例会被执行
  it('should return the index when present', function() {
   // ...
  });
 });
});

Dynamically Generating Tests(动态生成测试用例)

其实这个在很多其他的测试工具,如NUnit也会有,就是将测试用例的参数用一个集合代替,从而生成不同的测试用例。下面是具体的例子:

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}
 ];
 // 下面就会生成三个不同的测试用例,相当于写了三个it函数的测试用例。
 tests.forEach(function(test) {
  it('correctly adds ' + test.args.length + ' args', function() {
   var res = add.apply(null, test.args);
   assert.equal(res, test.expected);
  });
 });
});

Interfaces(接口)

Mocha的接口系统允许用户用不同风格的函数或者样式写他们的测试用例集合和具体的测试用例,mocha有BDD,TDD,Exports,QUnit和Require 风格的接口。

BDD - 这个是mocha的默认样式,我们在本文中的示例代码就是这样的格式。
其提供了describe(), context(), it(), before(), after(), beforeEach(), and afterEach()的函数,示例代码如下:

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

  describe('#indexOf()', function() {
   context('when not present', function() {
    it('should not throw an error', function() {
     (function() {
      [1,2,3].indexOf(4);
     }).should.not.throw();
    });
    it('should return -1', function() {
     [1,2,3].indexOf(4).should.equal(-1);
    });
   });
   context('when present', function() {
    it('should return the index where the element first appears in the array', function() {
     [1,2,3].indexOf(3).should.equal(2);
    });
   });
  });
 });

TDD - 提供了 suite(), test(), suiteSetup(), suiteTeardown(), setup(), 和 teardown()的函数,其实和BDD风格的接口类似(suite相当于describe,test相当于it),示例代码如下:

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

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

Exports - 对象的值都是测试用例集合,函数值都是测试用例。 关键字before, after, beforeEach, and afterEach 需要特别定义。
具体的示例代码如下:

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

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

QUnit - 有点像TDD,用suit和test函数,也包含before(), after(), beforeEach()和afterEach(),但是用法稍微有点不一样, 可以参考下面的代码:

function ok(expr, msg) {
 if (!expr) throw new Error(msg);
}

suite('Array');

test('#length', function() {
 var arr = [1,2,3];
 ok(arr.length == 3);
});

test('#indexOf()', function() {
 var arr = [1,2,3];
 ok(arr.indexOf(1) == 0);
 ok(arr.indexOf(2) == 1);
 ok(arr.indexOf(3) == 2);
});

suite('String');

test('#length', function() {
 ok('foo'.length == 3);
});

Require - 该接口允许我们利用require关键字去重新封装定义 describe ,it等关键字,这样可以避免全局变量。
如下列代码:

var testCase = require('mocha').describe;
var pre = require('mocha').before;
var assertions = require('mocha').it;
var assert = require('assert');

testCase('Array', function() {
 pre(function() {
  // ...
 });

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

上述默认的接口是BDD, 如果想使用其他的接口,可以使用下面的命令行:

mocha -ui  接口(TDD|Exports|QUnit...)

Reporters (测试报告/结果样式)

Mocha 支持不同格式的测试结果暂时,其支持 Spec, Dot Matrix,Nyan,TAP…等等,默认的样式为Spec,如果需要其他的样式,可以用下列命令行实现:

mocha --reporter 具体的样式(Dot Matrix|TAP|Nyan...)

Editor Plugins

mocha 能很好的集成到TextMate,Wallaby.js,JetBrains(IntelliJ IDEA, WebStorm) 中,这里就用WebStorm作为例子。 JetBrains提供了NodeJS的plugin让我们很好的使用mocha和nodeJs。 添加mocha 的相关的菜单,具体配置过程可以参考https://www.jetbrains.com/webstorm/help/running-mocha-unit-tests.html

这里就可以直接在WebStorm中运行,调试mocha的测试用例了。

NodeJs 相关文章推荐
nodejs简单实现中英文翻译
May 04 NodeJs
Nodejs Stream 数据流使用手册
Apr 17 NodeJs
基于NodeJS+MongoDB+AngularJS+Bootstrap开发书店案例分析
Jan 12 NodeJs
nodejs+websocket实时聊天系统改进版
May 18 NodeJs
NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】
Sep 06 NodeJs
nodejs使用redis作为缓存介质实现的封装缓存类示例
Feb 07 NodeJs
Nodejs实现爬虫抓取数据实例解析
Jul 05 NodeJs
PHPStorm中如何对nodejs项目进行单元测试详解
Feb 28 NodeJs
使用nodejs分离html文件里的js和css详解
Apr 12 NodeJs
nodejs实现日志读取、日志查找及日志刷新的方法分析
May 20 NodeJs
nodejs实现百度舆情接口应用示例
Feb 07 NodeJs
nodeJs项目在阿里云的简单部署
Nov 27 NodeJs
NodeJS测试框架mocha入门教程
Mar 28 #NodeJs
nodejs模块nodemailer基本使用-邮件发送示例(支持附件)
Mar 28 #NodeJs
angular2+nodejs实现图片上传功能
Mar 27 #NodeJs
深入nodejs中流(stream)的理解
Mar 27 #NodeJs
nodejs学习笔记之路由
Mar 27 #NodeJs
NodeJS处理Express中异步错误
Mar 26 #NodeJs
简单好用的nodejs 爬虫框架分享
Mar 26 #NodeJs
You might like
PHP面向接口编程 耦合设计模式 简单范例
2011/03/23 PHP
PHP的SQL注入过程分析
2012/01/06 PHP
php中FTP函数ftp_connect、ftp_login与ftp_chmod用法
2014/11/18 PHP
常见php数据文件缓存类汇总
2014/12/05 PHP
cnblogs TagCloud基于jquery的实现代码
2010/06/11 Javascript
Wordpress ThickBox 添加“查看原图”效果代码
2010/12/11 Javascript
jquery查找父元素、子元素(个人经验总结)
2014/04/09 Javascript
js实现特定位取反原理及示例
2014/06/30 Javascript
Javascript技术栈中的四种依赖注入小结
2016/02/27 Javascript
学习掌握JavaScript中this的使用技巧
2016/08/29 Javascript
js实现简单的碰壁反弹效果
2016/08/30 Javascript
Angularjs CURD 详解及实例代码
2016/09/14 Javascript
canvas 画布在主流浏览器中的尺寸限制详细介绍
2016/12/15 Javascript
js实现键盘自动打字效果
2016/12/23 Javascript
浅谈JavaScript的函数及作用域
2016/12/30 Javascript
微信小程序 页面跳转和数据传递实例详解
2017/01/19 Javascript
js代码延迟一定时间后执行一个函数的实例
2017/02/15 Javascript
angularjs实现时间轴效果的示例代码
2017/11/29 Javascript
Javascript和jquery在selenium的使用过程
2019/10/31 jQuery
D3.js 实现带伸缩时间轴拓扑图的示例代码
2020/01/20 Javascript
使用vue3重构拼图游戏的实现示例
2021/01/25 Vue.js
javascript实现倒计时提示框
2021/03/02 Javascript
[47:10]完美世界DOTA2联赛PWL S3 LBZS vs Rebirth 第二场 12.16
2020/12/18 DOTA
python有证书的加密解密实现方法
2014/11/19 Python
用Python的Django框架完成视频处理任务的教程
2015/04/02 Python
Python中设置变量访问权限的方法
2015/04/27 Python
Python函数参数匹配模型通用规则keyword-only参数详解
2019/06/10 Python
python3 tkinter实现点击一个按钮跳出另一个窗口的方法
2019/06/13 Python
python 单线程和异步协程工作方式解析
2019/09/28 Python
调用其他python脚本文件里面的类和方法过程解析
2019/11/15 Python
CSS3等相关属性制作分页导航实现代码
2012/12/24 HTML / CSS
JBL美国官方商店:扬声器、耳机等
2019/12/01 全球购物
办公室前台岗位职责
2014/01/04 职场文书
成龙霸王洗发水广告词
2014/03/14 职场文书
教师考核评语大全
2014/12/31 职场文书
工作感想范文
2015/08/07 职场文书