Jest (Pleasant Testing Framework)

1 What is Jest

Jest is Facebook’s open source JavaScript testing framework that automatically integrates assertions, JSDom, coverage reports, and all the other testing tools that developers need. It is a near-zero configuration testing framework.

2 the installation

Use YARN to install Jest

yarn add --dev jest
Copy the code

Or NPM:

npm install --save-dev jest
Copy the code

3 Simple Trial

Let’s write our first summation function, sum.js

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

Next create the test case:

const sum = require('./sum');

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

Configure the test startup script in package.json:

{
    "scripts": {
        "test": "jest"}}Copy the code

Finally, by running yarn test or NPM run test,Jest will print the following message:

PASS./sum.test.js adds 1 + 2 to equal 3 (5ms)Copy the code

4 verifier

Ordinary matching apparatus

The simplest test is to compare an exact match

test(`two plus tow is four`, () => {
    expect(2 + 2).toBe(4);
})
Copy the code

Truthiness

In testing, sometimes you need to accurately distinguish between undefined, null, and false, and sometimes you don’t. Jest satisfies all of them.

  • ToBeNull only matches null
  • ToBeUndefined only matches undefined
  • ToBeDefined is the opposite of toBeUndefined
  • ToBeTruthy matches any if statement that is true
  • ToBeFalsy matches any if statement that is false
test('null', () => {
  const n = null;
  expect(n).toBeNull();
  expect(n).toBeDefined();
  expect(n).not.toBeUndefined();
  expect(n).not.toBeTruthy();
  expect(n).toBeFalsy();
});

test('zero', () => {
  const z = 0;
  expect(z).not.toBeNull();
  expect(z).toBeDefined();
  expect(z).not.toBeUndefined();
  expect(z).not.toBeTruthy();
  expect(z).toBeFalsy();
});
Copy the code

digital

Most comparison numbers have equivalent matchers.

test('two plus two', () => { const value = 2 + 2; expect(value).toBeGreaterThan(3); Expect (value). ToBeGreaterThanOrEqual (3.5); expect(value).toBeLessThan(5); Expect (value). ToBeLessThanOrEqual (4.5); // toBe and toEqual are equivalentfor numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
});
Copy the code

For comparing floating point numbers for equality, use toBeCloseTo instead of toEqual, because you don’t want the test to depend on a small rounding error.

test('Add two floating point numbers'() => {const value = 0.1 + 0.2; / / expect (value). Place (0.3); ToBeCloseTo (0.3); toBeCloseTo(0.3); toBeCloseTo(0.3); // This will work});Copy the code

string

ToMatch regular expression string:

test('there is no I in team', () => {
  expect('team').not.toMatch(/I/);
});

test('but there is a "stop" in Christoph', () => {
  expect('Christoph').toMatch(/stop/);
});
Copy the code

Arrays and iterables

ToContain You can check whether an array or an iterable contains a particular item with toContain:

const shoppingList = [
  'diapers'.'kleenex'.'trash bags'.'paper towels'.'beer',];test('the shopping list has beer on it', () => {
  expect(shoppingList).toContain('beer');
  expect(new Set(shoppingList)).toContain('beer');
});
Copy the code

exception

The particular function under test throws an error, and when it is called, use toThrow.

function compileAndroidCode() {
  throw new ConfigError('you are using the wrong JDK');
}

test('compiling android goes as expected', () => {
  expect(compileAndroidCode).toThrow();
  expect(compileAndroidCode).toThrow(ConfigError);

  // You can also use the exact error message or a regexp
  expect(compileAndroidCode).toThrow('you are using the wrong JDK');
  expect(compileAndroidCode).toThrow(/JDK/);
});
Copy the code

Test asynchronous code

The callback

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

Promises

test('the data is peanut butter', () = > {return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});
Copy the code

.resloves/.rejects

test('the data is peanut butter', () = > {return expect(fetchData()).resolves.toBe('peanut butter');
});
Copy the code
test('the fetch fails with an error', () = > {return expect(fetchData()).rejects.toMatch('error');
});
Copy the code

Async/Await

test('the data is peanut butter', async () => {
  const data = await fetchData();
  expect(data).toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch('error'); }});Copy the code

6 Mounting and Unmounting (Setup and Teardown)

It is used for the preparation that needs to be done before the tests are run when you write them, and for the cleanup that needs to be done after the tests are run.

Repeat the Settings for multiple tests

beforeEach(() => {
  initializeCityDatabase();
});

afterEach(() => {
  clearCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});
Copy the code

One-time setting

beforeAll(() => {
  return initializeCityDatabase();
});

afterAll(() => {
  return clearCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});
Copy the code

scope

By default, before and after blocks can be applied to each test in the file. In addition, tests can be grouped using describe blocks. When the before and after blocks are inside a Describe block, they are only applicable to tests within that describe block.

// Applies to all tests in this file
beforeEach(() => {
  return initializeCityDatabase();
});

test('city database has Vienna', () => {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
  expect(isCity('San Juan')).toBeTruthy();
});

describe('matching cities to foods', () => {
  // Applies only to tests in this describe block
  beforeEach(() => {
    return initializeFoodDatabase();
  });

  test('Vienna <3 sausage', () => {
    expect(isValidCityFoodPair('Vienna'.'Wiener Schnitzel')).toBe(true);
  });

  test('San Juan <3 plantains', () => {
    expect(isValidCityFoodPair('San Juan'.'Mofongo')).toBe(true);
  });
});

Copy the code

MockFunction test (mockFunction)

Using mocks

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}

const mockCallback = jest.fn(x => 42 + x);
forEach([0, 1], mockCallback);

// The mock function is called twice
expect(mockCallback.mock.calls.length).toBe(2);

// The first argument of the first call to the function was 0
expect(mockCallback.mock.calls[0][0]).toBe(0);

// The first argument of the second call to the function was 1
expect(mockCallback.mock.calls[1][0]).toBe(1);

// The return value of the first call to the function was 42
expect(mockCallback.mock.results[0].value).toBe(42);
Copy the code

Return value of the mock

const myMock = jest.fn();
console.log(myMock());
// > undefined

myMock
  .mockReturnValueOnce(10)
  .mockReturnValueOnce('x')
  .mockReturnValue(true); console.log(myMock(), myMock(), myMock(), myMock()); / / > 10,'x'.true.true
Copy the code

7 Snapshot

You can use the snapshot function to create snapshots for components for comparison after adjustment

Use React’s Test Renderer and Jest’s snapshot feature to interact with components to get render results and generate snapshot files:

import React from 'react';
import Link from '.. /Link.react';
import renderer from 'react-test-renderer';

test('Link changes the class when hovered', () => {
  const component = renderer.create(
    <Link page="http://www.facebook.com">Facebook</Link>,
  );
  let tree = component.toJSON();
  expect(tree).toMatchSnapshot();

  // manually trigger the callback
  tree.props.onMouseEnter();
  // re-rendering
  tree = component.toJSON();
  expect(tree).toMatchSnapshot();

  // manually trigger the callback
  tree.props.onMouseLeave();
  // re-rendering
  tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});
Copy the code