Request a free audit

Specialising in Digital positioning and marketing, we tackle challenging questions that yield tangible value. By the end, you’ll receive actionable tips ready for immediate implementation. Take advantage of our complimentary, no-obligation complete audits.

Social media audit Problem definition workshop ISO/SOC readiness

 

Use Jest for React App Testing

Date
October 27, 2017
Hot topics 🔥
Tech Insights
Contributor
David Roman
Use Jest for React App Testing

By Dmitry Shishko, Full Stack Developer.

 


In this article, I would like to share my experience with testing the React application. There are quite a few tools for testing applications, such as MochaJasmineJestKarmaChai and many more.

What I plan on covering is:

  • Component testing
  • User behavior testing

For the purposes of this article, I would like to investigate the use of Jest from Facebook, because it’s pretty simple to configure and use.

What Jest allows:

  1. Quick installation and configuration
  2. Running tests before deployment or in development mode
  3. Unit test, functional tests and snapshot tests
  4. Code coverage reports integrate simply with sonarqube
  5. Mock data for test

Together with Jest, I use Enzyme. Enzyme is a tool that makes it easy to manipulate DOM elements.

Installing Jest

What we install:

  • Jest — Jest is used by Facebook to test all JavaScript code including React applications.
  • Babel-jest — setup to use all ES6 features and React specific syntax.
  • Enzyme — Enzyme is a JavaScript testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output.
  • Enzyme-to-json — convert the Enzyme wrapper to format compatibility with Jest.
  • Nock — allows you to mock objects in your test files.
  • Redux-mock-store — used to mock our Redux store.

Then set up your package.json

Running the test is done using the command “npm run test” or “npm run test:watch”.

A handy tool that takes a snapshot of your component, and then checks the UI for changes.

List.js

ListRow.js

./__tests__/List.spec.js

Jest automatically generates a snapshot.

./__tests__/__snaphots__/List.spec.js.snap

As you can see, everything is quite simple.

Testing connected components.

I have a Login Page.js component. In this component I have structured the page to include a header, fields for email and password, and the login button. File structure like this:

Сreate the file ./__tests/LoginPage.spec.js

import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
import ConnectedLogin, { LoginPage } from '../LoginPage';
import LogoHeader from '../LogoHeader';
// Props, which we pass to component
const props = {
 localization: {
  common: {
   login: 'Login',
  },
  form: {
   email: 'Email',
   password: 'Password',
   emailRequired: 'Email is required',
   emailNotValid: 'Email is not valid',
   passwordRequired: 'Password is required',
  }
 },
 currentLang: 'en',
 login: () => {},
};
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
describe('>>>LoginPage - Shallow Render REACT COMPONENTS', () => {
 let wrapper;
beforeEach(() => {
  wrapper = itmes => shallow(<LoginPage {…itmes} />);
 });
// Create 'snapshot'
 it('+++ render LoginPage correctly, snapshot', () => {
  const component = wrapper(props);
  expect(toJson(component)).toMatchSnapshot();
 });
// Check render comonent without errors
 it('+++ render the DUMB component', () => {
  expect(wrapper(props).length).toEqual(1);
 });
// Check the existence of header component
 it('+++ contains LogoHeader component', () => {
  expect(wrapper(props).find(LogoHeader).length).toBe(1);
});
// Simple behavior for fill in the fields
 it('+++ simulate change email, password input', () => {
  const component = wrapper(props);
  const eventEmail = {
   target: { name: 'email', value: 'test@test.com' }
  };
  const eventPassword = {
   target: { name: 'password', value: '123456' }
  };
  component.find('[name="email"]').simulate('change', eventEmail);
  component.find('[name="password"]')
   .simulate('change', eventPassword);
expect(component.state('email')).toEqual('test@test.com');
  expect(component.state('password')).toEqual('123456');
  expect(component.find('[name="email"]').prop('value'))
   .toEqual('test@test.com');
  expect(component.find('[name="password"]').prop('value'))
   .toEqual('123456');
 });
 
 it('+++ triggers submit handler with valid form data', () => {
  const mockFn = jest.fn(() => Promise.resolve({}));
  props.login = mockFn;
  const component = wrapper(props)
    .setState({ email: 'test@test.com', password: '123456' });
  component.find('button[type="button"]').simulate('click');

  expect(mockFn).toHaveBeenCalledWith({ 
    email: 'test@test.com', password: '123456'
  });
  expect(mockFn).toHaveBeenCalledTimes(1);
 });
});
// setup for testing connected component
const mockStore = configureStore(middlewares);
const localization = {
 data: {
  localization: {
   common: {
    login: 'Login',
   },
   form: {
    email: 'Email',
    password: 'Password',
    emailRequired: 'Email is required',
    emailNotValid: 'Email is not valid',
    passwordRequired: 'Password is required',
   },
  },
  type: 'en',
 },
};
const auth = {
 current: {},
};
const initialState = {
 localization,
 auth,
};
describe('>>>LoginPage - REACT-REDUX (Shallow + passing the {store} directly)’, () => {
 let container;
 beforeEach(() => {
  const store = mockStore(initialState);
  container = shallow(<ConnectedLoginPage store={store} />);
 });
it('+++ render the connected(SMART) component', () => {
  expect(container.length).toEqual(1);
 });

 it('+++ check Prop matches with initialState', () => {
  expect(container.prop('currentLang')).toEqual('en');
 });
});
describe('>>>LoginPage — REACT-REDUX (Mount + wrapping in Provider)', () => {
 let container;
 beforeEach(() => {
  const store = mockStore(initialState);
  container = mount(
   <Provider store={store}><ConnectedLoginPage /></Provider>
  );
 });
it('+++ render the connected(SMART) component', () => {
  expect(container.find(ConnectedLoginPage).length).toEqual(1);
 });
it('+++ contains class component', () => {
  expect(container.find('.grayBackground').length).toBe(1);
 });

 it('+++ check Prop matches with initialState', () => {
  expect(container.find(LoginPage).prop('currentLang'))
   .toEqual(initialState.localization.data.type);
 });
});

Test coverage

For code coverage, you only need to run the command "npm run test:coverage".

Conclusion

As you can see, it’s not particularly difficult to cover components with testing. I really hope this article will help you improve your coding.

Thanks for reading!

David Roman

David is one our marketing gurus. He loves working with content but has a good eye for marketing analytics as well. Creativity is what drives him, photography being one of his passions.

Working Machines

An executive’s guide to AI and Intelligent Automation. Working Machines takes a look at how the renewed vigour for the development of Artificial Intelligence and Intelligent Automation technology has begun to change how businesses operate.