Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ChristopherWerby/d6e774c67b808c63f6a546ec1a53936d to your computer and use it in GitHub Desktop.
Save ChristopherWerby/d6e774c67b808c63f6a546ec1a53936d to your computer and use it in GitHub Desktop.
React, Jest, Enzyme - React Component Testing Boilerplate
//Christopher Werby
//2017-12-10
//Testing Boilerplate used with create-react-app, for testing a React App's components using Jest and Enzyme.
//Credit to Stephen Scott, https://medium.freecodecamp.org/the-right-way-to-test-react-components-548a4736ab22
//
//Initial setup after create-react-app is installed:
//npm install --save-dev enzyme enzyme-adapter-react-16 react-test-renderer jest-enzyme
//Create a file at /src/setupTests.js:
// import { configure } from 'enzyme';
// import Adapter from 'enzyme-adapter-react-16';
// import 'jest-enzyme';
// configure({ adapter: new Adapter() });
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow, mount } from 'enzyme'; //shallow not being used in boilerplate
import XXX from '../XXX';
import { mockProps } from '../__mocks__/props';
describe("NAME_OF_XXX_COMPONENT_IN_ENGLISH", () => {
let props;
let component;
const getComponent = () => {
if (!component) {
component = mount(
<XXX {...props} />
);
}
return component;
}
beforeEach( () => {
//reset props and component each time.
props = Object.assign( {}, mockProps );
component = undefined;
});
afterEach( () => {
//If necessary, can unmount the component to clear the virtual DOM.
});
//Basic "smoke test"
it('renders without crashing', () => {
component = getComponent();
});
//Snapshot test; props.endTime is different everytime it runs, so it is zeroed out.
it('renders snapshot', () => {
props.endTime = 0;
component = getComponent();
expect(component).toMatchSnapshot();
});
//FURTHER TESTS GO HERE. WHAT'S BELOW IS JUST SAMPLE CODE TO BE REPLACED
describe('classes', () => {
test('classes includes [item, start] when play is false', () => {
props.play = false;
component = getComponent();
expect(component.find('.item').length).toBeTruthy();
expect(component.find('.start').length).toBeTruthy();
});
test('classes does not include [disable] when play is false', () => {
props.play = false;
component = getComponent();
expect(component.find('.disable').length).toBeFalsy();
});
test('classes includes [item, start] when play is true', () => {
props.play = true;
component = getComponent();
expect(component.find('.item').length).toBeTruthy();
expect(component.find('.start').length).toBeTruthy();
});
test('classes includes [disable] when play is true', () => {
props.play = true;
component = getComponent();
expect(component.find('.disable').length).toBeTruthy();
});
});
describe('on click behavior', () => {
test('startGame function fired on click', () => {
const spy = props.startGame;
component = getComponent();
component.find('button').simulate('click');
expect(spy).toHaveBeenCalled();
})
});
describe('display of score inside li', () => {
test('display 25 when score is 25', () => {
props.score = 25;
component = getComponent();
expect( component.find('li').text() ).toBe('25');
});
});
//different from above because the render method just returns a value.
describe('display of score', () => {
test('display 0 when score is 0', () => {
props.score = 0;
component = getComponent();
expect( component.text() ).toBe('0');
});
test('display 25 when score is 25', () => {
props.score = 25;
component = getComponent();
expect( component.text() ).toBe('25');
});
});
test('classes includes [bonk] when bonk is true', () => {
component = getComponent();
component.setState({bonk: true});
expect(component.find('.bonk').length).toBeTruthy();
});
test('class [bonk] removed after time', () => {
//Jest can use Fake Timers
jest.useFakeTimers();
component = getComponent();
component.find('div.mole').simulate('click');
//All timers are run. (There's also runOnlyPendingTimers)
jest.runAllTimers();
component.update(); //Make sure that setState has executed.
expect(component.find('.bonk').length).toBeFalsy();
});
//Snapshot test. Note that props.endTime is different everytime it runs, so it is zeroed out. This makes the snapshots consistent from run to run.
it('renders a bunch of holes', () => {
props.endTime = 0;
component = getComponent();
expect(component).toMatchSnapshot();
});
//By putting displayName="Hole" as a prop on a component, can then read that string to see if it rendered the proper number of times. But displayName, by default, is inferred as the name of the component, so usually not necessary.
it('renders the number of holes requested in options', () => {
const holesRequested = parseInt(options.holesCount);
component = getComponent();
expect(component.find('Hole').length).toBe(holesRequested);
});
//Tests method on component
describe('Random Hole Function', () => {
it('returns a number in range that is not the same as previous', () => {
const holesRequested = parseInt(options.holesCount);
component = getComponent();
let lastNum = 0;
let thisNum = 0;
for (var i=1; i<1000; i++) {
//note use of `instance()` to call method of component
thisNum = component.instance().randomHole();
expect(thisNum).toBeGreaterThan(0);
expect(thisNum).toBeLessThanOrEqual(holesRequested);
expect(thisNum).not.toEqual(lastNum);
lastNum = thisNum;
}
});
});
test('classes include [up] when the hole is active', () => {
props.activeHole = 0;
props.id = 3;
component = getComponent();
//Note Enzyme method to update the props.
component.setProps({activeHole: 3});
expect(component.find('.up').length).toBeTruthy();
});
it('returns {2,45,45} for input 9945', () => {
const input = 9945;
component = getComponent();
const obj = component.instance().parseSeconds(input);
expect({hours: 2, minutes: 45, seconds: 45}).toEqual(obj);
});
//In the function, the throw is: if( inSeconds !== parseInt(inSeconds) ) throw new Error('Must use a integer number of seconds for timer.');
it('throws error for input 9945.9', () => {
const input = 9945.9;
component = getComponent();
expect( () => component.instance().parseSeconds(input) ).toThrow(Error);
});
//Pulls the state value from the component to test a function.
describe('Update Score Function', () => {
it('increments the score by one when called', () => {
component = getComponent();
component.setState({score: 10});
component.instance().updateScore();
expect(component.instance().state.score).toBe(11);
});
});
//Tests render method
describe('Render Method', () => {
it('renders one Headline component', () => {
component = getComponent();
expect(component.find('Headline').length).toBe(1);
});
it('renders one Controls component', () => {
component = getComponent();
expect(component.find('Controls').length).toBe(1);
});
it('renders one Holes component', () => {
component = getComponent();
expect(component.find('Holes').length).toBe(1);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment