博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nodejs测试:从0到90(理论篇)
阅读量:6122 次
发布时间:2019-06-21

本文共 8193 字,大约阅读时间需要 27 分钟。

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

写在前面的

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

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

一个好的测试

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

  • 覆盖75%+

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

  • 可重复执行

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

  • 保持独立

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

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

无论是单个测试用例,还是集成测试,必须要保证测试执行足够的快。

测什么

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

怎么测

又是一个非常大的话题,本文仅从个人角度给出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 允许你使用任何你想用的断言库,包括:

  • BDD style shown throughout these docs
  • expect() style assertions
  • expect(), assert() and should style assertions
  • c-style self-documenting assert()
  • 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等)提供了 , 可以直接运行或调试 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

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

提供对 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):是否每个语句都执行了

是 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, 至于为什么,我还没有搞明白。

断言

目前比较流行的断言库包括:

  • BDD style shown throughout these docs
  • expect() style assertions
  • expect(), assert() and should style assertions
  • c-style self-documenting assert()
  • the extensible BDD assertion toolkit

这些断言库除了风格上略有不同外,实现在功能几乎完全一致,可根据个人喜好或应用需求选择。

下一篇:

转载地址:http://fizua.baihongyu.com/

你可能感兴趣的文章
HDU 4377 Sub Sequence[串构造]
查看>>
云时代架构阅读笔记之四
查看>>
WEB请求处理一:浏览器请求发起处理
查看>>
Lua学习笔记(8): 元表
查看>>
PHP经典算法题
查看>>
LeetCode 404 Sum of Left Leaves
查看>>
醋泡大蒜有什么功效
查看>>
hdu 5115(2014北京—dp)
查看>>
数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)...
查看>>
PHP读取日志里数据方法理解
查看>>
第五十七篇、AVAssetReader和AVAssetWrite 对视频进行编码
查看>>
Vivado增量式编译
查看>>
一个很好的幻灯片效果的jquery插件--kinMaxShow
查看>>
微信支付签名配置正确,但返回-1,调不出支付界面(有的手机能调起,有的不能)...
查看>>
第二周例行报告
查看>>
vue实现点击展开,点击收起
查看>>
如何使frame能居中显示
查看>>
第k小数
查看>>
构建之法阅读笔记三
查看>>
写给对前途迷茫的朋友:五句话定会改变你的人生
查看>>