65.9K
CodeProject 正在变化。 阅读更多。
Home

NodeJS 测试:从 0 到 90

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2017年1月25日

CPOL

9分钟阅读

viewsIcon

6162

NodeJS 测试覆盖率:我如何从 0% 到 90%

最近,我负责一个基于 NodeJS 的应用程序。在这个过程中,我不得不学习许多新东西,其中之一就是测试。一开始,我写不了多少代码,更不用说测试它了。那时,我的测试覆盖率是 0%。一段时间后,特别是当我看到 npm 上优秀库的测试覆盖率达到 100% 时,我下定决心要学习 NodeJS 测试。目前,应用程序的测试覆盖率是 90% 以上。这就是我如何从 0 到 90 的。要实现剩下的 10% 我还有很长的路要走,我将在未来讨论这个问题。

前言

对于开发人员来说,测试的重要性毋庸置疑。你经常听到,“没有测试,就没有发布”,或者“没有测试,就没有重构”。然而,在实践中,总是存在某种测试问题。例如:

  • 没有编写测试用例:通常,开发人员认为编写测试用例是浪费时间,或者他们可能不知道如何编写测试用例。
  • 混乱的测试用例:测试用例可能写得随意,没有标准化,没有覆盖率,也没有集成。
  • 编写测试用例可能比不编写更好:这些类型的测试用例通常不可读,不可维护,不可信,不可重复,不能独立运行(非常依赖某些环境或条件),或者不能执行(通常只在开发期间执行,以后不会执行,或者执行非常慢,或者没有人愿意执行)。

一个好的测试

如果你和一千个人交谈,你会听到至少两千种测试概念和方法。这使得很难定义什么是好的测试。随着我的团队对测试承担更多责任,我们形成了一个简单的理念。它包括以下原则:

  • 覆盖率:75%+

衡量一个好测试最重要的指标是测试了多少代码(其覆盖率)。75% 是最低标准。这个标准对 Java 来说基本可行,但不适用于 NodeJS。JavaScript 是一种弱类型动态语言,没有编译阶段。这意味着许多错误只能在应用程序运行时才能发现。因此,我们需要更高的覆盖率。最好能达到 100%,但目前我的个人标准是 90% 以上。

  • 可重复执行

每个测试用例都应该能够在任何环境中重复执行并产生相同的结果。只有这样,你才能真正信任你的测试并用它来发现实际的 bug。这也是集成测试的最低要求。

  • 独立性

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

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

无论是单个测试用例还是集成测试,都必须确保测试能够足够快地执行。

测试什么

测试什么这个问题主要根据实际需求、业务、成本、语言等因素来回答。然而,也有一些共性。单元测试原则(该链接为中文)提供了参考原则,但我在这里不会进一步讨论。

如何测试

这是一个非常广泛的问题。在这篇文章中,我将只根据自己的观点讨论 NodeJS 测试工具和方法。

概述

框架

NodeJS 测试框架有很多。目前,使用最广泛的是 Mocha。在本文中,我将详细描述 Mocha 并简要介绍其他几个框架。除非另有说明,本文中的所有示例都基于 Mocha。

Mocha

一个简单、灵活、有趣的 JavaScript 测试框架,适用于 node.js 和浏览器。

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));
    });
  });
});

Run

$ mocha

输出结果

1 test complete (1ms)

用途

断言

Mocha 允许你使用任何你想要的断言库,包括:

钩子 (Hooks)

Mocha 提供了钩子函数,例如 before()after()beforeEach()afterEach()。这些用于设置测试前条件和清理测试,示例如下:

专属测试或跳过测试

专属测试允许只测试指定的测试集或用例。对于专属测试,你只需在测试集或用例前添加 .only(),如下所示:

跳过测试类似于 JUnit 的 @Ignore。它们用于跳过或忽略指定的测试集或用例。为此,只需在测试集或用例前添加 .skip(),如下所示:

describe('Array', function(){
  describe.skip('#indexOf()', function(){
    ...
  })
})

编辑器插件

除了使用 Mocha 提供的命令行,你还可以使用编辑器插件(此链接无效)来运行测试用例。目前支持以下插件:

  • TextMate
  • JetBrains
  • Wallaby.js
  • Emacs

让我们以 JetBrains 为例。JetBrains 为其 IDE 套件(IntelliJ IDEA、WebStorm 等)提供了 NodeJS 支持。它可以直接运行或调试 Mocha 测试用例。基本过程:

  • 安装 NodeJS 插件(如果未安装,则必须已安装 IntelliJ IDEA 或 WebStorm):前往 Preferences > Plugins,找到并安装名为 NodeJS 的插件。
  • 添加 Mocha 测试。前往 Edit Configuration 并添加 Mocha,如下所示:

运行或调试测试。此插件允许你运行或调试目录、文件、测试集和测试用例。运行测试,如下所示:

其他

下面,我将列出其他几个基于 NodeJS 的测试框架或工具。它们可用于在 NodeJS 或你的浏览器中测试 JavaScript 代码。我将不再详细讨论它们,因此要了解更多信息,请参阅官方文档。

Jasmine

一个行为驱动开发 JavaScript 测试框架

Jasmine 是一个行为驱动开发框架,用于测试 JavaScript 代码。它不依赖于任何其他 JavaScript 框架。它不需要 DOM。而且它具有清晰、明确的语法,因此你可以轻松编写测试。

要了解更多信息,请参阅官方文档

Karma Runner

为开发人员带来高效的测试环境

Karma 的主要目标是为开发人员带来高效的测试环境。这个环境是他们不必设置大量配置,而是开发人员可以编写代码并从他们的测试中获得即时反馈的地方。要了解更多信息,请参阅官方文档。工具

Mocha 提供了一个基本的测试框架,但在某些情况下,你可能还需要使用其他辅助工具。下面,我提供一个常用工具列表。

SuperTest

SuperTest 为 HTTP 测试提供高级抽象。这大大简化了基于 HTTP 的测试。

此模块的动机是为 HTTP 测试提供高级抽象,同时仍然允许你使用 super-agent 提供的低级 API。

安装

$ 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);
  });
});

要了解更多信息,请参阅文档

代码覆盖率是软件测试中使用的度量。它描述了程序源代码中被测试的部分和程度。这个比例就是代码覆盖率:这个指标有四个维度:

  • 行覆盖率:每行是否被执行
  • 函数覆盖率:每个函数是否被调用
  • 分支覆盖率:每个 if 代码块是否被执行
  • 语句覆盖率:每个语句是否被执行

Istanbul 是最流行的 JavaScript 代码覆盖率工具。

快速入门

安装

$ npm install -g istanbul

Run

最简单的方法

$ cd /path/to/your/source/root
$ istanbul cover test.js

Output operation results:

..
  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 是一个覆盖率报告,你可以在浏览器中打开,如下所示:

Istanbul

调式

两种常见的测试模式是 TDD 和 BDD。

TDD(测试驱动开发)

TDD 是敏捷开发中使用的核心实践和技术。它也是一种设计方法论。TDD 的原则如下:在开发功能代码之前,编写单元测试用例代码。此测试代码将决定需要编写哪些产品代码。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));
        });
    });
});
Start to write the factorial logic, as shown below.
module.exports = function (n) {
    if (n < 0) return NaN;
    if (n === 0) return 1;
 
    return n * factorial(n - 1);
};

现在,运行测试用例,看看程序是否通过测试。如果程序通过所有测试,则表明开发完成。如果未通过所有测试用例,则修复或修改被测逻辑,直到程序通过所有测试。

BDD(行为驱动开发)

BDD 是一种敏捷软件开发技术。它鼓励开发人员、QA 人员、非技术人员和商业参与者在软件项目中协作。这个过程从用户需求分析开始,并强调系统行为。它最显著的特点是使用书面的行为描述或规范来驱动软件开发。从外观上看,行为和规范描述与测试非常相似。然而,它们之间存在显著差异。

我们将继续使用上面介绍的阶乘来演示 BDD 模式测试。

从上面的例子中,我们可以看到 BDD 和 TDD 测试用例最大的区别在于措辞。BDD 测试用例读起来像普通的句子。因此,BDD 测试用例可以用作促进开发人员、QA 人员、非技术人员和商业参与者之间协作的工具。如果开发人员能够更流畅地阅读测试用例,他们自然能够编写更好、更全面的测试用例。

TDD 与 BDD

本质上和目的上,TDD 和 BDD 是相同的。它们只在实现上有所不同,并且安排了不同的讨论以改进整体敏捷开发系统。TDD 迭代地重复验证以确保敏捷开发。然而,它没有明确规定如何根据设计生成测试用例并确保测试用例的质量。另一方面,BDD 提倡使用清晰自然的语言来描述系统行为。这正好弥补了测试用例(系统行为)的准确性。

基本上所有基于 NodeJS 的库和应用程序都选择 BDD,尽管我不明白确切的原因。

断言

目前有几个流行的断言库:

除了风格上的细微差异,这些库或多或少都是相同的。你可以选择最适合你的偏好或应用程序需求的库。

通过这些测试框架运行我的 NodeJS 测试,我能够获得 90% 以上的测试覆盖率。要实现超过 90% 的测试覆盖率,请留意我未来不久的文章。

© . All rights reserved.