本文共 8193 字,大约阅读时间需要 27 分钟。
最近在负责一个基于nodejs的应用,在很多方面都经历了一个从无到有的过程,测试也是如此。刚开始时,代码都写不好,更别提测试了,那时测试为0。经历过一段时间后,尤其是看到 上优秀的库的测试覆盖率都在100%时,痛下决心开始学习 nodejs 的测试, 目前这个应用的测试覆盖率在90%+。这就是我的从0到90。剩下的10,还有很长的路,且待下回分解。
对于开发者,测试的重要性毋庸置疑, 所谓”无测试不上线“,”无测试不重构“等。但在实践的过程中,测试总是有这样的那样的问题,随便列举下:
一千个人眼中有至少二千种测试理念或方法,很难定义什么是一种好的测试。在团队一直负责测试的推进,我们的理念很简单,包括以下几个原则:
一个好的测试,最重要的衡量标准就是测了多少(覆盖率),75%是最低的标准。这个标准对java来说基本可行,但对nodejs不太适用,javascript是弱类型的、动态语言,没有编译阶段,很多错误只能在运行时才会被发现,所以需要更高的覆盖率,最好是100%,目前个人的标准是90%+。
每一个测试用例,无论在任何环境下,都应该可以反复执行,并产生相同的结果。只有这样,才能够相任你的测试,进而发现真正的bug。这也是集成测试最低要求。
一个测试用例只测试代码的某一方面,如一个分支,且不强依赖某些特定的环境或条件。
无论是单个测试用例,还是集成测试,必须要保证测试执行足够的快。
测试测什么主要依据具体的需求、业务、成本、语言等,但也有一定的共性, 给出了一些准则可供参考,这里不再讨论。
又是一个非常大的话题,本文仅从个人角度给出nodejs测试的工具及方法。
Nodejs 测试框架众多,目前使用最广的是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 允许你使用任何你想用的断言库,包括:
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 提供的命令行外,还可以使用 运行,目前支持了:
以JetBrains为例,JetBrain 为其 IDE 套件(IntelliJ IDEA, WebStorm等)提供了 , 可以直接运行或调试 mocha 测试用例。基本步骤:
NodeJS
的插件并安装;Edit Configuration
添加 Mocha
, 示例如下:以下列举几个基于 nodejs 的测试框架或工具,它们可用于 nodejs 或 浏览器端javascript 代码的测试,不在本文讨论范围,详见官方文档。
A Behavior Driven Development JavaScript testing framework
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.
更多见 .
To bring a productive testing environment to developers
The main goal for Karma is to bring a productive testing environment to developers. The environment being one where they don't have to set up loads of configurations, but rather a place where developers can just write the code and get instant feedback from their tests.
更多见 .
mocha 提供了测试的基本框架,在特定的场景下的测试还需要其它工具做以辅助,这个列举几个常用的工具。
提供对 HTTP 测试的高度抽象, 极大地简化了基于 HTTP 的测试。
The motivation with this module is to provide a high-level abstraction for testing HTTP, while still allowing you to drop down to the lower-level API provided by super-agent.
$ npm install supertest --save-dev
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); });});
更多见
代码覆盖(Code coverage)是软件测试中的一种度量,描述程式中源代码被测试的比例和程度,所得比例称为代码覆盖率。它有四个测量维度:
是 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的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。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);};
此时再运行之前的测试用例,看其是否通过,如果全部通过则表示开发完成,如不通过则反复修改被测试的逻辑,直到全部的通过为止。
行为驱动开发是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、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和BDD本质和目标都是一致的。只是在实施方法上,进行了不同的探讨来完善整个敏捷开发的体系。TDD的迭代反复验证是敏捷开发的保障,但没有明确如何根据设计产生测试,并保障测试用例的质量,而BDD倡导大家都用简洁的自然语言描述系统行为的理念,恰好弥补了测试用例(即系统行为)的准确性。
几乎所有基于Nodejs的库或应用都选择了BDD, 至于为什么,我还没有搞明白。
目前比较流行的断言库包括:
这些断言库除了风格上略有不同外,实现在功能几乎完全一致,可根据个人喜好或应用需求选择。
下一篇:
转载地址:http://fizua.baihongyu.com/