简介: 最近在负责一个基于nodejs的应用,在很多方面都经历了一个从无到有的过程,测试也是如此。刚开始时,代码都写不好,更别提测试了,那时测试为0。经历过一段时间后, 测试覆盖率在90%+。这就是我的从0到90。剩下的10,还有很长的路,且待下回分解。
最近在负责一个基于nodejs的应用,在很多方面都经历了一个从无到有的过程,测试也是如此。刚开始时,代码都写不好,更别提测试了,那时测试为0。经历过一段时间后,尤其是看到 npm 上优秀的库的测试覆盖率都在100%时,痛下决心开始学习 nodejs 的测试, 目前这个应用的测试覆盖率在90%+。这就是我的从0到90。剩下的10,还有很长的路,且待下回分解。
写在前面的
对于开发者,测试的重要性毋庸置疑, 所谓”无测试不上线“,”无测试不重构“等。但在实践的过程中,测试总是有这样的那样的问题,随便列举下:
- 不写测试。一般是觉得写测试浪费时间,也有可能是不会写;
- 乱写测试。随意的写,没有规范,没有覆盖率,没有集成;
- 写了跟没写一样。这样的测试通常是不可读、不可维护、不可信任,不可重复或独立运行(强依赖某些特定的环境或条件)、不执行(通常只是开发的时候执行一下,之后再也不会执行,或者执行太慢,没有人愿意执行)。
一个好的测试
一千个人眼中有至少二千种测试理念或方法,很难定义什么是一种好的测试。在团队一直负责测试的推进,我们的理念很简单,包括以下几个原则:
- 覆盖75%+
一个好的测试,最重要的衡量标准就是测了多少(覆盖率),75%是最低的标准。这个标准对java来说基本可行,但对nodejs不太适用,javascript是弱类型的、动态语言,没有编译阶段,很多错误只能在运行时才会被发现,所以需要更高的覆盖率,最好是100%,目前个人的标准是90%+。
- 可重复执行
每一个测试用例,无论在任何环境下,都应该可以反复执行,并产生相同的结果。只有这样,才能够相任你的测试,进而发现真正的bug。这也是集成测试最低要求。
- 保持独立
一个测试用例只测试代码的某一方面,如一个分支,且不强依赖某些特定的环境或条件。
- 可读、可维护、可信任
- 快快快
无论是单个测试用例,还是集成测试,必须要保证测试执行足够的快。
测什么
测试测什么主要依据具体的需求、业务、成本、语言等,但也有一定的共性,单元测试准则 给出了一些准则可供参考,这里不再讨论。
怎么测
又是一个非常大的话题,本文仅从个人角度给出nodejs测试的工具及方法。
概述
框架
Nodejs 测试框架众多,目前使用最广的是Mocha。本文详细说明下 Mocha, 并简要介绍几种其它框架。本文的示例,如无特别说明,都是基于Mocha.
Mocha
A simple, flexible, fun JavaScript test framework for node.js and the browser.
Mocha 是一个功能丰富的Javascript测试框架,它能运行在Node.js和浏览器中,支持BDD、TDD式的测试。
快速开始
-
安装
npm install -g mocha
-
写一个简的测试用例
var assert = require('chai').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 允许你使用任何你想用的断言库,包括:
- should.js BDD style shown throughout these docs
- expect.js expect() style assertions
- chai expect(), assert() and should style assertions
- better-assert c-style self-documenting assert()
- unexpected the extensible BDD assertion toolkit
钩子 Hooks
Mocha 提供了 before()
, after()
, beforeEach()
, afterEach()
等 hooks,用于设置前置条件及清理测试,示例如下:
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
})
专用测试 或 跳过测试
专用测试允许只测试指定的测试集或用例,只需在测试集或用例前加.only()
,示例如下:
describe('Array', function(){
describe.only('#indexOf()', function(){
...
})
})
跳过类似于 junit 的 @Ignore
, 用于跳过或忽略指定的测试集或用例,只需在测试集或用例前加.skip()
,示例如下:
describe('Array', function(){
describe.skip('#indexOf()', function(){
...
})
})
编辑器插件
除了使用 Mocha 提供的命令行外,还可以使用 编辑器插件 运行,目前支持了:
- TextMate
- JetBrains
- Wallaby.js
- Emacs
以JetBrains为例,JetBrain 为其 IDE 套件(IntelliJ IDEA, WebStorm等)提供了 NodeJS, 可以直接运行或调试 mocha 测试用例。基本步骤:
- 安装 NodeJS 插件(如果没有安装的话,默认IntelliJ IDEA, WebStorm都已安装):通过 Preferences > Plugins 查找名为
NodeJS
的插件并安装; - 添加 mocha 测试。通过
Edit Configuration
添加Mocha
, 示例如下:
- 运行或调试。支持按目录、文件、测试集、测试用例等运行或调试,运行示例如下:
其它
以下列举几个基于 nodejs 的测试框架或工具,它们可用于 nodejs 或 浏览器端javascript 代码的测试,不在本文讨论范围,详见官方文档。
Jasmine
A Behavior Driven Development JavaScript testing framework
更多见 官方文档.
Karma Runner
To bring a productive testing environment to developers
更多见 官方文档.
工具
mocha 提供了测试的基本框架,在特定的场景下的测试还需要其它工具做以辅助,这个列举几个常用的工具。
SuperTest
SuperTest 提供对 HTTP 测试的高度抽象, 极大地简化了基于 HTTP 的测试。
安装
$ npm install supertest --save-dev
使用示例
-
简单的 HTTP 请求
var request = require('supertest');
describe('GET /user', function() { it('respond with json', function(done) { request(app) .get('/user') .set('Accept', 'application/json') .expect('Content-Type', /json/) .expect(200, done); }); });
-
上传文件
request(app) .post('/') .field('name', 'my awesome avatar') .attach('avatar', 'test/fixtures/homeboy.jpg') // ..
-
修改响应头或体
describe('GET /user', function() { it('user.name should be an case-insensitive match for "tobi"', function(done) { request(app) .get('/user') .set('Accept', 'application/json') .expect(function(res) { res.body.id = 'some fixed id'; res.body.name = res.body.name.toUpperCase(); }) .expect(200, { id: 'some fixed id', name: 'TOBI' }, done); }); });
更多见 文档
Istanbul
代码覆盖(Code coverage)是软件测试中的一种度量,描述程式中源代码被测试的比例和程度,所得比例称为代码覆盖率。它有四个测量维度:
- 行覆盖率(line coverage):是否每一行都执行了
- 函数覆盖率(function coverage):是否每个函数都调用了
- 分支覆盖率(branch coverage):是否每个if代码块都执行了
- 语句覆盖率(statement coverage):是否每个语句都执行了
Istanbul 是 JavaScript 语言最流行的代码覆盖率工具。
快速开始
-
安装
$ npm install -g istanbul
-
运行
最简单的方式:
$ cd /path/to/your/source/root
$ istanbul cover test.js
输出运行结果:
..
test/app/util/result.test.js
should static create
should be success
should be static success
should be error
should be static error
299 passing (13s)
[mochawesome] Report saved to /opt/source/node_modules/.mochawesome-reports/index.html
=============================== Coverage summary ===============================
Statements : 92.9% ( 1505/1620 )
Branches : 85.42% ( 410/480 )
Functions : 94.33% ( 133/141 )
Lines : 93.01% ( 1504/1617 )
================================================================================
Done
这条命令同时还生成了一个 coverage
子目录,其中的 coverage.json
文件包含覆盖率的原始数据,coverage/lcov-report
是可以在浏览器打开的覆盖率报告,如下图所示:
模式
常见的测试模式包括TDD, BDD。
TDD (Test Driven Development)
测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需求分析,设计,质量控制量化的过程。整体流程如下:
以一个阶乘的小程序为例。先写出的测试用例,如下所示。此时运行肯定会报错,因为被测试的程序还没有写。
var assert = require('assert'),
factorial = require('../index');
suite('Test', function (){
suite('#factorial()', function (){
test('equals 1 for sets of zero length', function (){
assert.equal(1, factorial(0));
});
test('equals 1 for sets of length one', function (){
assert.equal(1, factorial(1));
});
test('equals 2 for sets of length two', function (){
assert.equal(2, factorial(2));
});
test('equals 6 for sets of length three', function (){
assert.equal(6, factorial(3));
});
});
});
开始写阶乘的逻辑,如下所示。
module.exports = function (n) {
if (n < 0) return NaN;
if (n === 0) return 1;
return n * factorial(n - 1);
};
此时再运行之前的测试用例,看其是否通过,如果全部通过则表示开发完成,如不通过则反复修改被测试的逻辑,直到全部的通过为止。
BDD (Behavior Driven Development)
行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。主要是从用户的需求出发,强调系统行为。其最显著的特征是,通过编写行为和规格说明来驱动软件开发。行为和规格说明看上去与测试十分相似,但它们之前还是有显著的不同。
还是以上面的阶乘为例,BDD模式的测试用例如下:
var assert = require('assert'),
factorial = require('../index');
describe('Test', function (){
before(function(){
// Stuff to do before the tests, like imports, what not
});
describe('#factorial()', function (){
it('should return 1 when given 0', function (){
factorial(0).should.equal(1);
});
it('should return 1 when given 1', function (){
factorial(1).should.equal(1);
});
it('should return 2 when given 2', function (){
factorial(2).should.equal(2);
});
it('should return 6 when given 3', function (){
factorial(3).should.equal(6);
});
});
after(function () {
// Anything after the tests have finished
});
});
从示例上看,BDD的测试用例与TDD最大的不同在于其措辞,BDD读起来更像是一个句子。因此BDD的测试用例可作为开发者、QA和非技术人员或商业参与者之间的协同工具,对开发者来说,如果你可以非常流畅地读测试用例,自然能够写出更好、更全面的测试。
TDD vs BDD
TDD和BDD本质和目标都是一致的。只是在实施方法上,进行了不同的探讨来完善整个敏捷开发的体系。TDD的迭代反复验证是敏捷开发的保障,但没有明确如何根据设计产生测试,并保障测试用例的质量,而BDD倡导大家都用简洁的自然语言描述系统行为的理念,恰好弥补了测试用例(即系统行为)的准确性。
几乎所有基于Nodejs的库或应用都选择了BDD, 至于为什么,我还没有搞明白。
断言
目前比较流行的断言库包括:
- should.js BDD style shown throughout these docs
- expect.js expect() style assertions
- chai expect(), assert() and should style assertions
- better-assert c-style self-documenting assert()
- unexpected the extensible BDD assertion toolkit
这些断言库除了风格上略有不同外,实现在功能几乎完全一致,可根据个人喜好或应用需求选择。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!