An overview of the

In daily functional development, we rely on ourselves or QA to test our code. These operations are time consuming and are driven by the developers themselves. There is no way to provide a complete code quality report to third parties while developing libraries that rely on them.

Now you can use unit tests to improve the quality of your code. Below, I summarize my experiences and pitfalls in configuring and writing unit tests using Jest and sinon.js, and share them along the lines of configuring and writing unit tests from scratch.

In this article, you can solve the following problems:

  • What are Jest and sinon.js?
  • How do I configure Jest with sinon.js to write unit tests?
  • How to solve common problems encountered in unit testing?

What are Jest and sinon.js

Jest is a FaceBook unit testing library for JavaScript that provides apis for assertions, function emulation, and more to test your own business logic code.

Sinon.js is a JavaScript library for independent testing and simulation. It is often used in unit test writing to simulate related requests such as HTTP.

Why not use other unit testing frameworks

In the initial framework selection, I first tried the AVA framework that can parallel test and greatly improve the speed of unit test. It can meet everyday common requirements such as utils toolset testing, and can also configure sinon.js for HTTP simulation testing.

However, when dealing with the webpack Alias problem, The extremely complex configuration of the official issue does not solve the problem of the Cannot Find Module (babel-plugin-webpack-loaders, one of the plug-ins to solve this problem, recommends using Jest directly).

In Jest, you can easily identify the WebPack alias used in a file with a few simple configurations, which are described in the following sections.

For other testing frameworks such as Mocha or Chai, there is no specific understanding, so there is not much comment here.

How do I configure Jest with sinon.js to write unit tests?

Jest configuration

Installing dependency packages

To use Jest, you first need to install it by executing the following command:

npm install jest -D
Copy the code

If a. Babelrc file exists in your project (using Babel 6), you will need to install several additional packages, regardless of whether the code you are testing compiles from Babel:

npm install babel-jest babel-core regenerator-runtime -D
Copy the code

If you are using Babel 7, you will need to install the following packages:

npm install babel-jest 'Babel - core @ ^ 7.0.0-0' @babel/core regenerator-runtime -D
Copy the code

Package. json file configuration

After installing the dependencies, if you have jest configuration items that need to be set, you can also configure the following fields in the package.json file:

{
  "jest": {}}Copy the code

The. Babelrc file only needs to save the previous configuration and takes effect without any modification.

Sinon. Js configuration

Dependency package Installation

With Jest installed and configured, let’s take a look at sinon.js. To use sinon.js, we first need to install:

npm install sinon -D
Copy the code

After the configuration is complete, import it in the required place, as shown below:

const sinon = require('sinon');
Copy the code

In my project, I mainly use sinon.js to simulate HTTP requests. In the sinon.js documentation, there is a section devoted to emulating the XMLHttpRequest object, and in the next chapter, we will give a brief introduction to the use of sinon.js in the project.

Writing unit tests

In this chapter, we will provide a detailed explanation of how to write a unit test file, including:

  • Synchronization function test
  • Asynchronous function testing
  • HTTP test

We also give a brief introduction to the Jest and sinon.js apis. If you need to use other apis, you can read the documentation for Jest and sinon.js.

With the above three tests, we can almost cover all the code in our existing project.

Synchronization function test

Testing synchronous functions is the easiest part of the process. We can test the return value of the function as well as the higher-order functions passed in. Let’s look at a specific example.

Source code file, a pure function:

// user.js
export default function(obj) {
    return 'hjava';
}

export function handleUserData(callback) {
    callback('hjava');
}
Copy the code

A unit test file written against the source code file above:

// user.test.js
import userFunc, {handleUserData} from './user';

// test is a registered global method
test('user', () => {
    expect(userFunc()).toBe('hjava'); UserFunc = 'hjava'
    
    let callback = jest.fn(); // jest is a registered global variable
    handleUserData(callback);

    expect(callback.mock.calls.length).toBe(1); // Check that the callback function is called once
    expect(callback.mock.calls[0] [0]).toBe('hjava'); // The first argument to callback is 'hjava'.
});
Copy the code

As you can see from the above example, for a pure function of synchronization, we can verify its functionality through a very simple unit test model.

Asynchronous function testing

There are two main types of asynchronous functions — Callback and Promise. Both of these methods are very simple, and we will introduce them in detail. See the testing asynchronous code in the Jest documentation for more details.

The Callback method

// user.js
export default function(callback) {
    setTimeout((a)= >{
        callback({username: 'hjava'});
    }, 1000);
}
Copy the code
// user.test.js
import userFunc from './user';

test('user', () => {
    userFunc((data) = > {
        expect(data).toEqual({username: 'hjava'}); // beEqual()
    });
});
Copy the code

Promise way

// user.js
export default function(callback) {
    return Promise.resolve({username: 'hjava'});
}

Copy the code
// user.test.js
import userFunc from './user';

test('user', () => {
    userFunc().then((data) = > {
        expect(data).toEqual({username: 'hjava'});
    });
});
Copy the code

HTTP test

In testing HTTP request parameters, we need to simulate an XMLHttpRequest object to intercept HTTP requests and retrieve request data. Sinon.js can do just that. Here’s an example of the logic:

// user.js
export default function(callback) {
    this.sendRequest('/user/get', callback); // Send a request to retrieve user data, and execute the callback function on success
}
Copy the code
// user.test.js
import Sinon from 'sinon';
import userFunc from 'user';

let XHR;
let requests = [];
// beforeEach is a function provided by Jest that is executed once beforeEach test execution
beforeEach((a)= > {
    XHR = sinon.useFakeXMLHttpRequest(); // Create a mock XMLHttpRequest object

    XHR.onCreate = function (xhr) {
        requests.push(xhr);
    };
});

AfterEach is a function provided by Jest that is executed once afterEach test execution
afterEach((a)= > {
    XHR.restore();
});

test('user', () = > {let callback = jest.fn();

    HTTPCommon.deleteRemoteSession({
        data: {},
        success: callback
    });

    expect(requests.length).toBe(1);

    requests[0].respond(200, {"Content-Type": 'application/json'}, 'hjava'); // Simulate the return value

    expect(callback.mock.calls[0] [0]).toBe('hjava');
});
Copy the code

How to solve common problems encountered in unit testing?

In this chapter, we summarize the following problems to introduce, hope you can solve the same problem quickly:

  • How do I count Jest unit test coverage
  • How to set the unit test file not to use the local Babel configuration
  • How do I set up the unit test file to use the local Babel configuration
  • How do I handle webpack aliases referenced in my code

How do you measure unit test coverage?

Unlike AVA, which requires the use of SYC for calculations, Jest has built-in tools for measuring unit test coverage that require simple configuration. The configuration is as follows:

// Package. json {"jest": {"collectCoverage": true, // Enable unit test coverage "collectCoverageFrom": [// specify statistical unit test coverage file "**/ SRC /**.js"],}}Copy the code

How do I set unit test files not to use ES2015 configuration

If you have a. Babelrc file in your project and you don’t want the unit test file to be affected by the Babel file, you can add the transform field to the jest configuration item as follows:

// package.json
{
  "jest": {
    "transform": {}
  }
}
Copy the code

How do I set up unit tests using ES2015 configuration

If you need to compile with Babel using ES2015 in your unit test file, you may need to modify the configuration of the.babelrc file.

If you set the modules field to false in the.babelrc file, you’ll need to turn it back on in the test environment as follows:

// .babelrc
{
  "presets": [["env", {"modules": false}]],
  "env": {
    "test": {
      "presets": [["env"]]
    }
  }
}
Copy the code

If you are using Babel 7, you should set the presets field to @babel/env as follows:

// .babelrc
{
  "presets": [["env", {"modules": false}]],
  "env": {
    "test": {
      "presets": [["@babel/env"]]
    }
  }
}
Copy the code

How do I handle webpack aliases referenced in my code

If we use Webpack in our project, we will most likely use alias related attributes to define paths. However, in the unit testing framework, it does not recognize such a path and Cannot find module ‘XXX’ from ‘yyy’ is reported.

Unlike the AVA framework, which requires plug-in installation and complex configuration, we simply need to configure the moduleNameMapper property in Jest. Specific examples are as follows:

// webpack.config.js
{
    alias: {
        '@__dir':process.cwd()
    }
}
Copy the code
/ / package. Json {" jest ": {" moduleNameMapper" : {" @ __dir $" (. *) : "< rootDir > $1" / / regular matching method, corresponding webpack alias}}}Copy the code

conclusion

Writing tests is a good habit to get into.

Many people talk about quality control of their code, but don’t know how to do it. By the end of this article, you should have learned how to write a complete set of unit test cases against existing code from scratch.

If you have any questions, please leave a message or send a private message to communicate.

How Jest tests JavaScript code and how Sinon emulates XMLHttpRequest requests will be explained in the next few blog posts. If you are interested, follow me in the next few posts.

The appendix

  • Jest
  • Sinon.js
  • ava
  • Ava on configuration to resolve webpack alias issue
  • Mocha
  • Chai