Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Node.js

NodeJS Testing: From 0 to 90

5.00/5 (1 vote)
25 Jan 2017CPOL9 min read 5.8K  
NodeJS Testing Coverage Rate: How I went from 0% to 90%

Recently, I was responsible for a NodeJS-based application. In this process, I had to learn about many new things, one of which was testing. At the start, I was not able to write much code, much less test it. At that time, my testing coverage rate was 0%. After some time, and especially when I saw that the test coverage rate for excellent libraries on npm was 100%, I made a determined effort to learn NodeJS testing. Currently, the application's test coverage rate is 90%+. This is how I went from 0 to 90. I still have a long way to go to achieve the remaining 10% and I will discuss this in the future.

Preface

For developers, there is no doubt about the importance of testing. You often hear, "No testing, no release", or "No testing, no reconstruction". In practice, however, there is always some sort of testing problems. For example:

  • No test case is written: Generally, developers think that writing test cases is a waste of time, or maybe they do not know how to write test cases.
  • Messy test cases: Test cases may be written have hazardly, with no standardization, no coverage rate, or no integration.
  • Writing a test case may be no better than not writing one: These types of test cases are generally unreadable, cannot be maintained, are not credible, cannot be duplicated, cannot be run independently (are very reliant on certain environments or conditions), or cannot be executed (generally, they are only executed during development and will not be executed later, or are very slow in execution, or no one is willing to execute them).

A good test

If you talk to a thousand people, you will hear about at least two thousand testing concepts and methods. This makes it hard to define what is a good test. As my team has taken on more responsibility for testing, we have developed a simple philosophy. It includes the following principles:

  • Coverage: 75%+

The most important measure of a good test is how much code is tested (its coverage). 75% is the minimum standard. This standard is basically feasible for Java, but not suitable for NodeJS. JavaScript is a weakly typed dynamic language, with no compilation stage. This means that many errors can only be discovered when the application is run. Therefore, we need a higher coverage rate. It would be best to have 100%, but at present, my personal standard is 90%+.

  • Repeatable execution

Each test case should be able to be executed repeatedly in any environment and the same results should be produced. Only in this way can you really trust your test and use it to find actual bugs. This is also the minimum requirement for integrated testing.

  • Independence

A test case tests only one aspect of the code, such as one branch, and is not highly reliant on certain environments or conditions.

  • Readable, maintainable, trustable
  • Fast fast fast

Whether for a single test case or integrated testing, you must ensure that the test can be executed sufficiently quickly.

What to test

The question of what to test is mainly answered based on actual requirements, the business, costs, the language, and other factors. However, there are some commonalities. The Unit Testing Principles This link is in Chinese provide reference principles, but I will not discuss them further here.

How to test

This is a very broad question. In this article, I will only discuss NodeJS testing tools and methods based on my own opinions.

Overview

Frameworks

There are many NodeJS testing frameworks. Currently, the most widely used is Mocha. In this text, I will give a detailed description of Mocha and brief several other frameworks. In this article, all the examples are based on Mocha, unless otherwise specified.

Mocha

Image 1

A simple, flexible, fun JavaScript test framework for node.js and the browser.

Mocha is a JavaScript testing framework with a wide array of functions. It can run on Node.js or a browser and supports BDD and TDD testing.

Quick Start

Installation

npm install -g mocha

Write a simple test case

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

Output results:

1 test complete (1ms)

Use

Assertions

Mocha allows you to use any assertion library you want, including:

  • 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 provides hooks, such as before(), after(), beforeEach(), and afterEach(). These are used to set pre-test conditions and clean the test, as shown in the example below:

Dedicated tests or skip tests

Dedicated tests allow testing only specified test sets or cases. For a dedicated test, you just have to add .only() in front of the test set or case, as shown below:

Skip tests are similar to junit's @Ignore. They are used to skip or ignore a specified test set or case. To do this, just add .skip() in front of the test set or case, as shown below:

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

Editor plugins

Besides using the command line provided by Mocha, you can use editor plugins This link is bad to run test cases. Currently, the following plugins are supported:

  • TextMate
  • JetBrains
  • Wallaby.js
  • Emacs

Let's use JetBrains as an example. JetBrains provides NodeJS for its IDE kits (IntelliJ IDEA, WebStorm, etc.). It can directly run or debug Mocha test cases. Basic procedure:

  • Install the NodeJS plugin (if it is not installed, IntelliJ IDEA or WebStorm must already been installed): Go to Preferences > Plugins and find and install the plugin named NodeJS.
  • Add a Mocha test. Go to Edit Configuration and add Mocha, as shown below:

Image 2

Run or debug the test. This plugin allows you to run or debug directories, files, test sets, and test cases. Run the test as shown below:

Image 3

Other

Below, I will list several other NodeJS-based test frameworks or tools. They can be used to test JavaScript code in NodeJS or your browser. I will not discuss them in detail, so to learn more, refer to the official documentation.

Jasmine

Image 4

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 learn more, see the official documentation.

Karma Runner

Image 5

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. To learn more, see the official documentation. Tools

Mocha provides a basic framework for testing, but in certain scenarios, you may need to use other auxiliary tools as well. Below, I provide a list of common tools.

SuperTest

SuperTest provides high-level abstraction for HTTP testing. This substantially simplifies HTTP-based testing.

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.

Installation

$ npm install supertest --save-dev

Usage example

  • Simple HTTP request
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);
  });
});
  • Uploading files
request(app)
    .post('/')
    .field('name', 'my awesome avatar')
    .attach('avatar', 'test/fixtures/homeboy.jpg')
    // ..
  • Modifying response headers and bodies
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);
  });
});

To learn more, see the documentation.

Code coverage is a measurement used in software testing. It describes the proportion and extent of the program's source code that is tested. This proportion is the code coverage rate: This indicator has four dimensions:

  • Line coverage: Whether or not each line is executed
  • Function coverage: Whether or not each function is called
  • Branch coverage: Whether or not each if code block is executed
  • Statement coverage: Whether or not each statement is executed

Istanbul is the most popular JavaScript code coverage tool.

Quick Start

Installation

$ npm install -g istanbul

Run

Simplest method:

$ 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

This command will also generate a coverage subdirectory, in which the coverage.json file contains the original coverage data. The file coverage/lcov-report is a coverage report that you can open in your browser, as shown below:

Istanbul

Image 6

Modes

Two common testing modes are TDD and BDD.

TDD (Test Driven Development)

TDD is a core practice and technique used in agile development. It is also a design methodology. The principles of TDD are as follow: Prior to developing function code, write unit test case code. This test code will determine what product code needs to be written. The basic idea behind TDD is that testing can be used to drive the entire development process. However, TDD does not just involve testing work. It also involves needs analysis, design, and quality control quantization. The overall process is as follows:

Image 7

We will use a small factorial program as an example. First, write the test case, as shown below. Now, when you run it, it will certainly report an error. This is because the program to test has not been written.

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

Now, run the test case and see if the program passes the test. If the program passes all the tests, this indicates that development is complete. If it does not passes all the test cases, repair or modify the tested logic until the program passes the all the tests.

BDD (Behavior Driven Development)

BDD is an agile software development technique. It encourages developers, QA staff, non-technical staff, and commercial participants to collaborate in software projects. This process begins from an analysis of user needs and stresses system behaviors. Its most significant characteristic is that written descriptions of behaviors or specifications are used to drive software development. In appearance, behavior and specification descriptions are very similar to tests. However, there are significant differences.

We will continue using the factorial introduced above to demonstrate BDD mode testing:

From the example above, we can see that the biggest difference between the BDD and TDD test cases is wording. The BDD test cases read like normal sentences. Therefore, BDD test cases can be used as tools to facilitate the collaboration of developers, QA staff, non-technical staff, and commercial participants. If developers can more fluently read test cases, they will naturally be able to write better and more comprehensive test cases.

TDD vs BDD

In essence and purpose, TDD and BDD are the same. They differ only in implementation and different discussions are arranged in order to improve the overall agile development system. TDD iteratively repeats verification in order to ensure agile development. However, it does not clearly specify how to produce test cases based on the design and ensures the quality of the test cases. BDD, on the other hand, advocates the use of clear and natural language to describe system behaviors. This exactly offsets the accuracy of test cases (system behaviors).

Basically all NodeJS-based libraries and applications choose BDD, though I do not understand exactly why.

Assertions

Currently there are several popular assertion libraries:

  • 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

Except slight differences in style, these libraries are more or less the same. You can select the library that best suits your preferences or the needs of your application.

By running my NodeJS testing through these testing frame works I was able to get a test coverage of 90%+. For test coverage exceeding 90% please lookout for my future article in the near future.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)