×
Community Blog NodeJS Testing: From 0 to 90

NodeJS Testing: From 0 to 90

For developers, testing is important. You often hear, "No testing, no release", or "No testing, no reconstruction".

1f773db157e8dafa8aec3f9b521e7cfc544532c5

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 tes

t. 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

4f07de33bf71aed78e2820acc423969ba7159fda

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:

0923c08c3665df11777c936989a1e2362c60a077Run 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:db9d720741a45fb67cdb668ecf299cd551f74c4fOther

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.

Jasmineda3696d8ca988d9d720ff2bd24d3e0fc983f50b4

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 Runnerbd957b307bc34716ef60d3e9c7ea1b2b13965f5e

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

789a5af3012d5dc0f7494207416c983316caed40

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:a7cd559b9cf60cc77310ae7807b73a1b1dff2d30

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.

We need a conclusion to this article.  I will write one below but it needs to be checked with a developer.

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.

0 0 0
Share on

Alibaba Clouder

2,599 posts | 762 followers

You may also like

Comments