Summary: Recently responsible for a NodeJs-based application, it was a scratch in many ways, and so was testing. In the beginning, the code was bad, let alone the tests, which were zero. Over time, the test coverage was 90%+. That’s my zero to 90. The remaining 10, which is a long way off, will be decomposed next time.

Having recently been responsible for a NodeJs-based application, it’s been a scratch in many ways, as has testing. In the beginning, the code was bad, let alone the tests, which were zero. After some time, especially seeing that the best libraries on NPM have 100% test coverage, I decided to start learning nodeJS testing, which currently has 90%+ test coverage. That’s my zero to 90. The remaining 10, which is a long way off, will be decomposed next time.

It’s written on the front

For developers, the importance of testing is beyond doubt, the so-called “no test no live”, “no test no refactoring” and so on. But in practice, there are always some problems with testing, just to name a few:

  • Don’t write tests. Most people think writing tests is a waste of time, but they may not be able to write.
  • Scribble tests. Write casually, no specification, no coverage, no integration;
  • Writing is as good as not writing. Such tests are usually unreadable, unmaintainable, untrustworthy, unrepeatable, or run independently (heavily dependent on certain environments or conditions), and not executed (usually just once during development and never executed again, or executed so slowly that no one wants to execute them).

A good test

With at least 2,000 testing ideas or methods in the eyes of a thousand people, it’s hard to define what makes a good test. In the team has been responsible for testing the advancement, our philosophy is simple, including the following principles:

  • Covering 75% +

The most important measure of a good test is how much is tested, and 75% is the minimum. This standard works well for Java, but not so well for NodeJS. Javascript is a weakly typed, dynamic language with no compile phase and many errors can only be found at run time, so higher coverage is required, preferably 100%. My current standard is 90%+.

  • Repeatable execution

Every test case, in any environment, should be repeatable and produce the same results. Only then will you be able to control your tests and find real bugs. This is also the minimum requirement for integration testing.

  • To remain independent

A test case tests only one aspect of the code, such as a branch, and is not strongly dependent on some particular environment or condition.

  • Readable, maintainable, and trusted
  • Be quick

Whether it is a single test case or an integration test, it is important to ensure that the test execution is fast enough.

What test

What the test measures is mainly based on specific requirements, business, cost, language, etc., but there are certain commonalities. The unit test guidelines provide some guidelines for reference, which will not be discussed here.

How to measure

Again a very big topic, this article only gives the tools and methods for nodeJS testing from a personal perspective.

An overview of the

The framework

There are many Nodejs testing frameworks, and Mocha is currently the most widely used. This article takes a closer look at Mocha and briefly introduces several other frameworks. The examples in this article, unless otherwise noted, are based on Mocha.

Mocha

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

Mocha is a rich Javascript testing framework that runs in Node.js and browsers and supports BDD and TDD style testing.

Quick start

  • The 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

The following output is displayed:

  .

   1 test complete (1ms)
Copy the code

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
Hook Hooks

Mocha provides before(), after(), beforeEach(), afterEach() hooks for setting preconditions and cleaning tests, as shown in the following example:

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
})
Copy the code
Dedicated tests or skip tests

Dedicated tests allow you to test only specified test sets or use cases, preceded by.only(), as shown in the following example:

describe('Array', function(){ describe.only('#indexOf()', function(){ ... })})Copy the code

Skip junit-like @ignore, used to skip or Ignore a specified test set or use case by preceded by.skip(), as shown in the following example:

describe('Array', function(){ describe.skip('#indexOf()', function(){ ... })})Copy the code
Editor plug-in

In addition to using the command line provided by Mocha, it can also be run using the editor plug-in, which currently supports:

  • TextMate
  • JetBrains
  • Wallaby.js
  • Emacs

In the case of JetBrains, For example, JetBrain provides NodeJS for its IDE suite (IntelliJ IDEA, WebStorm, etc.) to run or debug mocha test cases directly. Basic steps:

  • Install NodeJS plugin (if not installed, IntelliJ IDEA and WebStorm are installed by default) : passPreferences > PluginsLook for,NodeJSPlug-ins and install;
  • Add mocha tests. throughEdit ConfigurationaddMocha, for example:

  • Run or debug. Supports running or debugging by directory, file, test set, and test case, as shown in the following example:

other

Here are a few nodeJs-based testing frameworks or tools that can be used to test NodeJS or browser-side javascript code. They are not covered in this article, but are detailed in the official documentation.

Jasmine

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.

See the official documentation for more.

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.

See the official documentation for more.

tool

Mocha provides the basic framework for testing, but other tools are needed for testing in specific scenarios. Here are a few commonly used tools.

SuperTest

SuperTest greatly simplifies HTTP-based testing by providing a high level of abstraction over HTTP 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.

The installation
$  npm install supertest --save-dev
Copy the code
Use the sample
  • 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); }); });

  • Upload a file

    request(app) .post(‘/’) .field(‘name’, ‘my awesome avatar’) .attach(‘avatar’, ‘test/fixtures/homeboy.jpg’) // ..

  • Modify the response header or body

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

See documentation for more

Istanbul

Code coverage is a measure in software testing that describes the percentage and extent to which the source Code of a program is tested. The obtained percentage is called Code coverage. It has four dimensions of measurement:

  • Line coverage: Is every line executed
  • Function coverage: Whether every function is called
  • Branch coverage: Whether every block of if code executes
  • Statement coverage: Whether each statement is executed

Istanbul is the most popular code coverage tool for the JavaScript language.

Quick start
  • The installation

    $ npm install -g istanbul

  • run

The simplest way:

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

Output running 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 : Branches: 85.42% (410/480) Functions: 94.33% (133/141) Lines: 93.01% (1504/1617) = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = the DoneCopy the code

This command also generates a coverage subdirectory, where the coverage.json file contains the raw coverage data. Coverage /lcov-report is a coverage report that can be opened in a browser, as shown below:

model

Common test modes include TDD and BDD.

TDD (Test Driven Development)

Test-driven development is a core practice and technology in agile development, as well as a design methodology. TDD works by writing unit test case code before developing functional code, which determines what product code needs to be written. The basic idea of TDD is to promote the whole development through testing, but test-driven development is not just a simple test work, but a process of quantifying requirements analysis, design and quality control. The overall process is as follows:

Take a factorial applet as an example. The test case written first is shown below. Running at this point is bound to report an error, because the program under 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)); }); }); });Copy the code

Start writing the factorial logic as follows.

module.exports = function (n) {
    if (n < 0) return NaN;
    if (n === 0) return 1;

    return n * factorial(n - 1);
};
Copy the code

At this point, run the previous test cases to see whether they pass. If all of them pass, the development is complete. If not, modify the tested logic repeatedly until all of them pass.

BDD (Behavior Driven Development)

Behavior-driven development is an agile software development technique that encourages collaboration between developers, QA, and non-technical or business participants in software projects. Mainly from the needs of users, emphasize system behavior. Its most striking feature is that it drives software development through writing behaviors and specifications. The behavior and specifications look very similar to testing, but they were significantly different before.

Take the above factorial as an example, the test of BDD mode is as follows:

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 }); });Copy the code

From an example point of view, the biggest difference between a BDD test case and TDD is its wording; BDD reads more like a sentence. BDD test cases can therefore serve as a collaborative tool between developers, QA, and non-technical or business participants. For developers, if you can read test cases very smoothly, you can naturally write better and more comprehensive tests.

TDD vs BDD

TDD and BDD are identical in nature and goal. Only in the implementation method, carried on the different discussion to perfect the whole agile development system. Iterative verification of TDD is the guarantee of agile development, but there is no clear way to generate tests according to the design and guarantee the quality of test cases. BDD advocates the concept that everyone uses simple natural language to describe system behavior, which just makes up for the accuracy of test cases (i.e., system behavior).

Almost every nodeJs-based library or application has chosen BDD, and I still don’t understand why.

assertions

Popular assertion libraries include:

  • 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

These assertion libraries differ slightly in style, but the implementation is almost identical in function and can be chosen according to personal preference or application requirements.