- Library to install
npm install --save-dev enzyme enzyme-adapter-react-16 - Setting up test environment to test
Reactcomponents withenzymeimport { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter(), disableLifecycleMethods: true }); - First
redtest for your componentimport React from 'react'; import { shallow } from 'enzyme'; describe('MyComponent', () => { it('should be rendered', () => { shallow(<MyComponent />); }); }); - Move component rendering to setup method (e.g.
beforeEach)describe('MyComponent', () => { let myComponent; beforeEach(() => { myComponent = shallow(<MyComponent />); }); }); - If your component has controls that wouldn’t be changed after re-rendering, move their look up to setup method (e.g.
beforeEach)describe('MyComponent', () => { let myComponent; let textInput; let submitButton; beforeEach(() => { myComponent = shallow(<MyComponent />); textInput = myComponent.find('input'); submitButton = myComponent.find('button'); }); }); - Move event simulation to helper functions, that makes your test more descriptive
describe('MyComponent', () => { // ... component setup const userEntersText = (text) => textInput.simulate('change', { targer: { value: text } }); const userClicksSubmitButton = () => submitButton.simulate('click'); const componentLabelText = () => myComponent.find('label').text(); it('changes label text from default to user input', () => { userEntersText('my favourite phrase'); userClicksSubmitButton(); expect(componentLabelText()).to.be.eq('my favourite phrase'); }); }); - If test depends on setting up additional state of your component, move it to inner
describefunctiondescribe('Outer', () => { // ... component setup const userEntersText = (text) => textInput.simulate('change', { targer: { value: text } }); const userClicksSubmitButton = () => submitButton.simulate('click'); const componentLabelText = () => myComponent.find('label').text(); // ... tests describe('Inner', () => { beforeEach(() => { userEntersText('my favourite phrase'); userClicksSubmitButton(); }); const userCleansText = () => userEntersText(''); it('resets label text to default when user cleans input and submits form', () => { userCleansText(); userClicksSubmitButton(); expect(componentLabelText()).to.be.eq('Default label text'); }); }); }); - Inject behaviour inside a component using Dependency Injection
describe('ToDoList', () => { const myPredictableTaskIdGenerator = () => 1; beforeEach(() => { todoList = shallow(<TodoList idGenerator={myPredictableTaskIdGenerator} />); }); }); - If your component has to save state to external storage, provide it using Dependency Injection
describe('ToDoList', () => { let testStorage; beforeEach(() => { testStorage = mockGlobalStorage(); todoList = shallow(<TodoList storage={testStorage} />); }); it('saves its state to storage', () => { createTaskWithName('Task #1'); createTaskWithName('Task #2'); createTaskWithName('Task #3'); const remountedTodoList = shallow(<TodoList storage={testStorage} />); expect(remountedTodoList.find('li')).to.have.length(3); }); }); - Previous test can be refactored to look up child component name (e.g.
Task) instead oflitag.import Task from 'path/to/Task/component'; describe('ToDoList', () => { // ... setup it('saves its state to storage', () => { createTaskWithName('Task #1'); createTaskWithName('Task #2'); createTaskWithName('Task #3'); const remountedTodoList = shallow(<TodoList storage={testStorage} />); expect(remountedTodoList.find(Task)).to.have.length(3); }); });