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 Mocha, Jasmine, Jest, Karma, Chai and many more.
What I plan on covering is:
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:
Together with Jest, I use Enzyme. Enzyme is a tool that makes it easy to manipulate DOM elements.
What we install:
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"
.
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!
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.