Jest

Alexa's Ranking: 23,096

Jest is a JavaScript Testing Framework with a focus on simplicity. Tests are parallelized by running them in their own processes to maximize performance.

To install Jest:


> npm install --save-dev jest

Tests in Jest are specified in a callback passed as the second parameter to the test() function. The first parameter is a string identifying the test.

Let's test it out:


// sum.jsfunction sum(a, b) {
  return a + b;}module.exports = sum;

// sum.test.jsconst sum = require('./sum');test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);});

// package.json// …{
  "scripts": {
    "test": "jest"
  }}

Running 'npm run test' prints the message:


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

By default, Jest will look for test files with any of the following popular naming conventions in the current folder and its sub-folders:

Files with a .js, .jsx, .ts or .tsx suffix in __tests__ folders.

Files with a .test.js suffix.

Files with a .spec.js suffix.

The test.js and spec.js files.

To watch for changes in the files and run the tests automatically:


> npm run test -- --watchAll

PASS
  __tests__/sum.x.js PASS
  ./sum.test.js PASS
  a/sum.spec.jsTest Suites:
  3 passed, 3 totalTests:
          3 passed, 3 totalSnapshots:
   0 totalTime:
           1.875 sRan all test suites.Watch Usage: Press w to show more.

Matchers Matchers Matchers

The simplest tests compare two expressions/values with expect():


test('two plus two is four', () => {
  expect(2 + 2).toBe(4);});test('object assignment', () => {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1, two: 2});});test('adding positive numbers is not zero', () => {
  for (let a = 1; a < 10; a++) {
    for (let b = 1; b < 10; b++) {
      expect(a + b).not.toBe(0);
    }
  }});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();});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 equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);});test('adding floating point numbers', () => {
  const value = 0.1 + 0.2;  //expect(value).toBe(0.3);           // This won't work because of rounding error
  expect(value).toBeCloseTo(0.3); // This works.});test('there is no I in team', () => {
  expect('team').not.toMatch(/I/);});test('but there is a "stop" in Christoph', () => {
  expect('Christoph').toMatch(/stop/);});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');});function compileAndroidCode() {
  throw new Error('you are using the wrong JDK');}test('compiling android goes as expected', () => {
  expect(compileAndroidCode).toThrow();
  expect(compileAndroidCode).toThrow(Error);  // You can also use the exact error message or a regexp
  expect(compileAndroidCode).toThrow('you are using the wrong JDK');
  expect(compileAndroidCode).toThrow(/JDK/);});

reference reference reference :expect(value)

expect.extend(matchers)

expect.anything()

expect.any(constructor)

expect.arrayContaining(array)

expect.assertions(number)

expect.hasAssertions()

expect.not.arrayContaining(array)

expect.not.objectContaining(object)

expect.not.stringContaining(string)

expect.not.stringMatching(string | regexp)

expect.objectContaining(object)

expect.stringContaining(string)

expect.stringMatching(string | regexp)

expect.addSnapshotSerializer(serializer)

.not

.resolves

.rejects

.toBe(value)

.toHaveBeenCalled()

.toHaveBeenCalledTimes(number)

.toHaveBeenCalledWith(arg1, arg2, ...)

.toHaveBeenLastCalledWith(arg1, arg2, ...)

.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)

.toHaveReturned()

.toHaveReturnedTimes(number)

.toHaveReturnedWith(value)

.toHaveLastReturnedWith(value)

.toHaveNthReturnedWith(nthCall, value)

.toHaveLength(number)

.toHaveProperty(keyPath, value?)

.toBeCloseTo(number, numDigits?)

.toBeDefined()

.toBeFalsy()

.toBeGreaterThan(number | bigint)

.toBeGreaterThanOrEqual(number | bigint)

.toBeLessThan(number | bigint)

.toBeLessThanOrEqual(number | bigint)

.toBeInstanceOf(Class)

.toBeNull()

.toBeTruthy()

.toBeUndefined()

.toBeNaN()

.toContain(item)

.toContainEqual(item)

.toEqual(value)

.toMatch(regexpOrString)

.toMatchObject(object)

.toMatchSnapshot(propertyMatchers?, hint?)

.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)

.toStrictEqual(value)

.toThrow(error?)

.toThrowErrorMatchingSnapshot(hint?)

.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)

Asynchrony Asynchrony Asynchrony

We should handle tests involving asynchronous functions with care. Assertions can be used to make sure expect() is called at least several times.


function fetchData(callback){
   return new Promise((resolve,reject)=>{
      resolve("peanut butter");
      callback("peanut butter");
   });}function fetchDataF(callback){
   return new Promise((resolve,reject)=>{
      reject("error");
      throw TypeError("error");
   });}function fetchDataT(callback){
   return new Promise((resolve,reject)=>{
     throw TypeError("error");
   });}test('the data is peanut butter', done => {
  function callback(data) {
    try {
      expect(data).toBe('peanut butter');
      done();
    } catch (error) {
      done(error);
    }
  }
  fetchData(callback);});test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });});test('the fetch fails with an error', () => {
  expect.assertions(1);
  return fetchDataF().catch(e => expect(e).
                                                     toMatch('error'));});test('the data is peanut butter', () => { return expect(fetchData()).resolves.toBe('peanut butter');});test('the fetch fails with an error', () => {
  return expect(fetchDataF()).rejects.toMatch('error');});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 fetchDataF();
  } catch (e) {
    expect(e).toMatch('error');
  }});test('the data is peanut butter', async () => {
  await expect(fetchData()).resolves.toBe('peanut butter');});test('the fetch fails with an error', async () => {
  await expect(fetchDataT()).rejects.toThrow('error');});

Setup & Teardown Setup & Teardown Setup & Teardown

You can call functions automatically before and after tests.


beforeAll(() => console.log('1 - beforeAll'));afterAll(() => console.log('1 - afterAll'));beforeEach(() => console.log('1 - beforeEach'));afterEach(() => console.log('1 - afterEach'));test('', () => console.log('1 - test'));describe('Scoped / Nested block', () => {
  beforeAll(() => console.log('2 - beforeAll'));
  afterAll(() => console.log('2 - afterAll'));
  beforeEach(() => console.log('2 - beforeEach'));
  afterEach(() => console.log('2 - afterEach'));
  test('', () => console.log('2 - test'));});describe('outer', () => {
  console.log('describe outer-a');
  describe('describe inner 1', () => {
    console.log('describe inner 1');
    test('test 1', () => {
      console.log('test for describe inner 1');
      expect(true).toEqual(true);
    });
  });
  console.log('describe outer-b');
  test('test 1', () => {
    console.log('test for describe outer');
    expect(true).toEqual(true);
  });
  describe('describe inner 2', () => {
    console.log('describe inner 2');
    test('test for describe inner 2', () => {
      console.log('test for describe inner 2');
      expect(false).toEqual(false);
    });
  });
  console.log('describe outer-c');});

Add '.only' to focus on a failing test and skip the other tests in a test file.


test.only('this will be the only test that runs', () => {
  expect(true).toBe(false);});test('this test will not run', () => {
  expect('A').toBe('A');});

Mock Functions Mock Functions Mock Functions

(special reference: https://jestjs.io/docs/en/mock-functions)

Mock functions allow you to test the links between code by erasing the actual implementation of a function and capturing calls to the function.

Imagine we wish to test the following function:


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

To test this function, we can use a mock callback:


const myMock = jest.fn(x => 42 + x);forEach([0, 1], myMock);myMock.mockReturnValueOnce(10)
            .mockReturnValueOnce('x')
            .mockReturnValue(true);test("Mock function",()=>{   // The mock function is called twice
   expect(myMock.mock.calls.length).toBe(6);   // The first argument of the first call to the function was 0
   expect(myMock.mock.calls[0][0]).toBe(0);   // The first argument of the second call to the function was 1
   expect(myMock.mock.calls[1][0]).toBe(1);   // The return value of the first call to the function was 42
   expect(myMock.mock.results[0].value).toBe(42);   // The function was instantiated twice
   expect(myMock.mock.instances.length).toBe(6);})console.log(myMock(), myMock(), myMock(), myMock());  // 10, 'x', true, true

All mock functions have this special .mock property, which is where data about how the function has been called and what the function returned is kept. The .mock property also tracks the value of this for each call, so it is possible to inspect this as well.

Suppose we have a class that fetches users from our API. The class uses axios to call the API then returns the data attribute which contains all the users:


// users.jsimport axios from 'axios';class Users {
  static all() {
    return axios.get('/users.json').then(resp => resp.data);
  }}export default Users;

Now, to test this method without actually hitting the API (and thus creating slow and fragile tests), we can use the jest.mock(...) function to automatically mock the axios module. Once we mock the module we can provide a mockResolvedValue for .get that returns the data we want our test to assert against. In effect, we are saying that we want axios.get('/users.json') to return a fake response.


// users.test.jsimport axios from 'axios';import Users from './users';jest.mock('axios');test('should fetch users', () => {
  const users = [{name: 'Bob'}];
  const resp = {data: users};
  axios.get.mockResolvedValue(resp);  // or you could use the following depending on your use case:  // axios.get.mockImplementation(() =>    //
                                                 Promise.resolve(resp))
  return Users.all().then(data =>
                                            expect(data).toEqual(users));});

The mockImplementationmockImplementation mockImplementation method is useful when you need to define the default implementation of a mock function that is created from another module:


// foo.jsmodule.exports = function () {  // some implementation;};// test.jsjest.mock('../foo');                  // this happens automatically with automockingconst foo = require('../foo');// foo is a mock functionfoo.mockImplementation(() => 42);foo();// > 42

When the mocked function runs out of implementations defined with mockImplementationOnce, it will execute the default implementation set with jest.fn (if it is defined):


const myMockFn = jest
  .fn(() => 'default')
  .mockImplementationOnce(() => 'first call')
  .mockImplementationOnce(() => 'second call');console.log(myMockFn(), myMockFn(),
                   myMockFn(), myMockFn());// > 'first call', 'second call', 'default', 'default'

const myObj = {
  myMethod: jest.fn().mockReturnThis(),};// is the same asconst otherObj = {
  myMethod: jest.fn(function () {
    return this;
  }),};

You can optionally provide a name for your mock functions, which will be displayed instead of "jest.fn()" in the test error output.


const myMockFn = jest
  .fn()
  .mockReturnValue('default')
  .mockImplementation(scalar => 42 + scalar)
  .mockName('add42');

These exist some custom matcher functions which make it easier to assert how mock functions are called:


// The mock function was called at least onceexpect(mockFunc).toHaveBeenCalled();// The mock function was called at least once with the specified argsexpect(mockFunc).toHaveBeenCalledWith(arg1, arg2);// The last call to the mock function was called with the specified argsexpect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);// All calls and the name of the mock is written as a snapshotexpect(mockFunc).toMatchSnapshot();

Snap Snap Snap shot shot shot

A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test.


import React from 'react';import renderer from 'react-test-renderer';import Link from '../Link.react';it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.facebook.com">Facebook</Link>)
    .toJSON();
  expect(tree).toMatchSnapshot();});

The first time this test is run, Jest creates a snapshot file that looks like this:


exports[`renders correctly 1`] = `<a
  className="normal"
  href="http://www.facebook.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}>
  Facebook</a>`;

Packages Packages Packages There are standalone packages that can be used to support specific features of Jest.

jest-changed-files identifies modified files in a git/hg repository.

jest-diff is a tool for visualizing changes in data.

jest-dockblock extracts and parses the comments at the top of a JavaScript file.

jest-get-type identifies the primitive type of any JavaScript value.

jest-validate validates configurations submitted by users.

jest-worker is used for parallelization of tasks.

pretty-format converts any JavaScript value into a human-readable string.


const prettyFormat = require('pretty-format');const val = {object: {}};val.circularReference = val;val[Symbol('foo')] = 'foo';val.map = new Map([['prop', 'value']]);val.array = [-0, Infinity, NaN];console.log(prettyFormat(val));