Nodejs测试:从0到90(理论篇)

简介: 最近在负责一个基于nodejs的应用,在不少方面都经历了一个从无到有的过程,测试也是如此。刚开始时,代码都写很差,更别提测试了,那时测试为0。经历过一段时间后, 测试覆盖率在90%+。这就是个人从0到90。剩下的10,还有很长的路,且待下回分解。javascript

最近在负责一个基于nodejs的应用,在不少方面都经历了一个从无到有的过程,测试也是如此。刚开始时,代码都写很差,更别提测试了,那时测试为0。经历过一段时间后,尤为是看到 npm 上优秀的库的测试覆盖率都在100%时,痛下决心开始学习 nodejs 的测试, 目前这个应用的测试覆盖率在90%+。这就是个人从0到90。剩下的10,还有很长的路,且待下回分解。html

写在前面的

对于开发者,测试的重要性毋庸置疑, 所谓”无测试不上线“,”无测试不重构“等。但在实践的过程当中,测试老是有这样的那样的问题,随便列举下:java

  • 不写测试。通常是以为写测试浪费时间,也有多是不会写;
  • 乱写测试。随意的写,没有规范,没有覆盖率,没有集成;
  • 写了跟没写同样。这样的测试一般是不可读、不可维护、不可信任,不可重复或独立运行(强依赖某些特定的环境或条件)、不执行(一般只是开发的时候执行一下,以后不再会执行,或者执行太慢,没有人愿意执行)。

一个好的测试

一千我的眼中有至少二千种测试理念或方法,很难定义什么是一种好的测试。在团队一直负责测试的推动,咱们的理念很简单,包括如下几个原则:node

  • 覆盖75%+

一个好的测试,最重要的衡量标准就是测了多少(覆盖率),75%是最低的标准。这个标准对java来讲基本可行,但对nodejs不太适用,javascript是弱类型的、动态语言,没有编译阶段,不少错误只能在运行时才会被发现,因此须要更高的覆盖率,最好是100%,目前我的的标准是90%+。git

  • 可重复执行

每个测试用例,不管在任何环境下,都应该能够反复执行,并产生相同的结果。只有这样,才可以相任你的测试,进而发现真正的bug。这也是集成测试最低要求。github

  • 保持独立

一个测试用例只测试代码的某一方面,如一个分支,且不强依赖某些特定的环境或条件。npm

  • 可读、可维护、可信任
  • 快快快

不管是单个测试用例,仍是集成测试,必需要保证测试执行足够的快。json

测什么

测试测什么主要依据具体的需求、业务、成本、语言等,但也有必定的共性,单元测试准则 给出了一些准则可供参考,这里再也不讨论。小程序

怎么测

又是一个很是大的话题,本文仅从我的角度给出nodejs测试的工具及方法。浏览器

概述

框架

Nodejs 测试框架众多,目前使用最广的是Mocha。本文详细说明下 Mocha, 并简要介绍几种其它框架。本文的示例,如无特别说明,都是基于Mocha.

Mocha

mocha_logo

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, 示例以下:

add_mocha_test

  • 运行或调试。支持按目录、文件、测试集、测试用例等运行或调试,运行示例以下:

run_mocha_test

其它

如下列举几个基于 nodejs 的测试框架或工具,它们可用于 nodejs 或 浏览器端javascript 代码的测试,不在本文讨论范围,详见官方文档。

Jasmine

jasmine_logo

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.

更多见 官方文档.

Karma Runner

karma_logo

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 提供了测试的基本框架,在特定的场景下的测试还须要其它工具作以辅助,这个列举几个经常使用的工具。

SuperTest

SuperTest 提供对 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
复制代码
使用示例
  • 简单的 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 是能够在浏览器打开的覆盖率报告,以下图所示:

icov

模式

常见的测试模式包括TDD, BDD。

TDD (Test Driven Development)

测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码以前,先编写单元测试用例代码,测试代码肯定须要编写什么产品代码。TDD的基本思路就是经过测试来推进整个开发的进行,但测试驱动开发并不仅是单纯的测试工做,而是把需求分析,设计,质量控制量化的过程。总体流程以下:

tdd_flowchart

以一个阶乘的小程序为例。先写出的测试用例,以下所示。此时运行确定会报错,由于被测试的程序尚未写。

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

这些断言库除了风格上略有不一样外,实如今功能几乎彻底一致,可根据我的喜爱或应用需求选择。

相关文章
相关标签/搜索