An overview of the

Needless to say, for a primer on the benefits of front-end unit testing, see the React Native Unit Test blog link. In the field of software testing, testing is mainly divided into unit testing, integration testing and functional testing.

  • Unit Testing: In computer programming, Unit Testing (English: Unit Testing) is also known as module Testing. It is the test work of verifying the correctness of the program module (the smallest Unit of software design). A program unit is the smallest testable part of an application. In procedural programming, a unit is a single program, function, procedure, etc. For object-oriented programming, the smallest unit is a method, including methods in a base class (superclass), an abstract class, or a derived class (subclass).
  • Integration testing, also called assembly testing or joint testing. On the basis of unit test, all modules are assembled into subsystems or systems according to the design requirements (such as according to the structure diagram) for integration test.
  • Functional test is to verify the functions of the product. According to the functional test cases, test item by item to check whether the product meets the functions required by users.

There are many front-end testing frameworks: Mocha, Jasmine, Ava, TestCafe and JEST, all of which have their own fields and characteristics. The JEST framework we adopted has the following characteristics:

  • Adaptability: Jest is modular, extensible, and configurable;
  • Sandbox and Fast: Jest virtualizes JavaScript environments, emulates browsers, and executes in parallel;
  • Snapshot testing: Jest can quickly write tests for snapshots or other serialized values of the React tree, providing a rapidly updated user experience.
  • Promises and Async /await are supported;
  • Automatically generate static analysis results: display not only test case execution results, but also statement, branch, function coverage, and so on.

The installation

# yarn
yarn add --dev jest

# npm
npm install --save-dev jest
Copy the code

We write a sum.js of the tested file as follows:

function sum(a, b) {
  return a + b;
}
module.exports = sum;
Copy the code

Next, we add a test file named sum.test.js, noting that we follow the naming rules of xxx.test.js.

Const sum = the require (". / the sum');
test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); });Copy the code

Built-in assertion library

Assertions are typically used by program developers themselves, and during development tests, to determine that certain expected results will be performed under certain logical conditions. The Jest framework has a rich set of assertion statements built in. For details, see Jest’s Expect. Here are some common ones:

.toBe(value)
.toHaveBeenCalled()
.toBeFalsy()
.toEqual(value)
.toBeGreaterThan(number)
.toBeGreaterThanOrEqual(number)
Copy the code

For example, here is a file under test called hook.js.

export default class Hook {

    constructor() {
        this.init();
    }

    init() {
        this.a = 1;
        this.b = 1;
    }

    sum() {
        returnthis.a + this.b; }}Copy the code

Hook. Js mainly realizes the function of adding two numbers, and then we write a test file Hook.

import Hook from '.. /src/hook';

describe('hook', () => { const hook = new Hook; // Data is restored before each test case is executed, so the following two tests can pass. beforeEach( () => { hook.init(); })test('test hook 1', () => {
        hook.a = 2;
        hook.b = 2;
        expect(hook.sum()).toBe(4);
    })
    test('test hook 2', () => { expect(hook.sum()).toBe(2); // Test pass})})Copy the code

Then, run the yarn jest command on the console to run the unit test. The result is displayed when the unit test is completed. Such as:

Lifecycle hooks

Jest testing provides some lifecycle apis for testing that help us do some processing at the beginning and end of each case. This way, when you run some data-related tests, you can prepare some data before the test and clean up the test data after the test. You can refer to the official global API for this section.

Here are four main lifecycle hooks:

  • AfterAll (fn, timeout): afterAll(fn, timeout): execute fn afterAll tests in the current file. If fn is a promise, jest will wait for timeout milliseconds, default 5000;
  • AfterEach (fn, timeout): fn is executed afterEach test. Timeout has the same meaning as above.
  • BeforeAll (fn, timeout): like afterAll, differing in that it is executed beforeAll tests start;
  • BeforeEach (fn, timeout): like afterEach, differing in that it is executed beforeEach test begins;
BeforeAll(() => {
  console.log('before all tests to excute ! ')
})

BeforeEach(() => {
  console.log('before each test ! ')
})

AfterAll(() => {
  console.log('after all tests to excute ! ')
})

AfterEach(() => {
  console.log('after each test ! ')
})

Test('test lifecycle 01', () => {
  expect(1 + 2).toBe(3)
})

Test('test lifecycle 03', () => {
  expect(2 + 2).toBe(4)
})
Copy the code

mock

A mock test is a way of creating a dummy object to continue testing for objects that are not easy to construct or retrieve. Mock functions typically provide the following three features:

  • Capture function calls;
  • Set the return value of the function;
  • Change the internal implementation of a function;

jest.fn()

Jest.fn () is the easiest way to create Mock functions, and if the internal implementation of the function is not defined, jest.fn() returns undefined as the return value. For example, there are two tested codes every.js and foreach.js. The code is as follows: every.js

function every(array, predicate) {
  let index = -1
  const length = array == null ? 0 : array.length
  while (++index < length) {
    if(! predicate(array[index], index, array)) {return false}}return true
}
export default every
Copy the code

foreach.js

function foreach(arr, fn) {
    for(let i = 0, len = arr.length;  i < len; i++) {
        fn(arr[i]);
    }
}

module.exports = foreach;
Copy the code

Here is the code for the test case mock.test.js file:

import foreach from '.. /foreach';
import every from '.. /every';

describe('mock test', () => {
    it('test foreach use mock'Const fn = jest.fn(); const fn = jest.fn(); const fn = jest.fn(); foreach([1, 2, 3], fn); // test the mock function toBe called three times expect(fn.mock.calls.length).tobe (3); // Expect (fn.mock. Calls [2][0]).tobe (3); }) it('test every use mock return value', () => {
        const fn = jest.fn();
        fn
            .mockReturnValueOnce(true)
            .mockReturnValueOnce(false);

        const res = every([1, 2, 3, 4], fn);
        expect(fn.mock.calls.length).toBe(2);
        expect(fn.mock.calls[1][1]).toBe(1);
    })

    it('test every use mock mockImplementationOnce', () => {
        const fn = jest.fn((val, index) => {
            if (index == 2) {
                return false;
            }
            return true; }); const res = every([1, 2, 3, 4], fn); expect(fn.mock.calls.length).toBe(3); expect(fn.mock.calls[1][1]).toBe(1); })})Copy the code

Manual mock

You can test your code manually by ignoring module dependencies. For example, there is a test file sum2.js.

function sum2(a, b) {
    if (a > 10) return a * b;
    return a + b;
}

export default sum2;
Copy the code

To mock a sum2.js file, create a new folder named __mock__ in the sum2.js directory and create a new file with the same name sum2.js. Mock returns 100.

export default function sum2(a, b) {
    return 100;
}
Copy the code

Then, create a new mock_file.test.js test file.

jest.mock('.. /sum2');
import sum2 from '.. /__mock__/sum2';

it('test mock sum2', () => {// We expect(sum2(1, 11111)).tobe (100); })Copy the code

Asynchronous test

In actual development, it is common to encounter asynchronous JavaScript code. When there is code running asynchronously, Jest needs to know if the code it is currently testing is complete before it can move to another test, that is, the test case must be run after the test object is finished. Jest’s asynchronous tests fall into three main categories:

  • The done function
  • return promise
  • async/await

An example of done is as follows:

function fetchData(call) {
  setTimeout(() => {
    call('peanut butter1')
  },1000);
}

test('the data is peanut butter', (done) = > {function callback(data) {
    expect(data).toBe('peanut butter');
    done()
  }
  fetchData(callback);
});

Copy the code

Since the SuperAgent library supports promises and async/await, for example, actual project development may involve Promises (pre-ES6 writing) and async/await (the latest writing), and you can write test code according to the actual situation.

import superagent from 'superagent';


const target = 'http://www.baidu.com';

describe('test promise async', () => {

    it('test done'.done => {
        superagent.get(target).end((err, res) => {
            expect(res).toBeTruthy();
            done(a); }); }); it('test promise', () = > {return superagent.get(target).then((res) => {
            expect(res).toBeTruthy();
        });
    });

    it('test async/await', async () => {
        const res = await superagent.get(target);
        expect(res).toBeTruthy();
    });
});
Copy the code

Note that when using the SuperAgent framework for asynchronous testing, make sure your project has superAgent dependencies installed.

Snapshot

When the snapshot test is run for the first time, the rendering results of UI components under different conditions are saved as a snapshot file. Each subsequent snapshot test is compared with the first one unless the yarn test – -u command is run to delete the snapshot file. For example, there is a file called reactcomp.js.

import React from 'react';

export default class reactComp extends React.Component {
    render() {
        return<div> I am react component </div>}}Copy the code

Then, write a test case file, reactcomp.test.js.

import React from 'react';
import renderer from 'react-test-renderer';

import RC from '.. /reactComp';

test('react-comp snapshot test', () => {
    const component = renderer.create(<RC />);
    let tree = component.toJSON();
    expect(tree).toMatchSnapshot();
})

test('react-comp snapshot test2', () => {
    const component = renderer.create(<RC />);

    let tree = component.toJSON();
    expect(tree).toMatchSnapshot();
})
Copy the code

When you run the test command, a __snapshots__ directory is generated in the test directory. In this directory, a snapshot file is generated in the following format:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`react-comp snapshot test1 '] = '<div> I'm react component </div>'; exports[`react-comp snapshottest2 1 '] = '<div> I am react component </div>';Copy the code

If the code under test is updated normally, you can use the “jest –updateSnapshot” command to update the cache file again.

React Native Unit test Jest