Read time: 13 minutes
1. The importance of writing tests in projects
2. What are Jest and Enzyme?
Facebook ReactJS tests are done with Jest but they aren't the only ones using it. Many other companies love it and some of them are high-profile names. Facebook, Instagram, and Revolut use Jest to test their code, which makes some people deem it the best ReactJS testing framework.
3. Installation and configuration
import "@testing-library/jest-dom"; import { configure } from "enzyme"; import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; configure({ adapter: new Adapter() });
Figure 1 – Configuration of Enzyme to use the adapter
4. Code Testing
4.1 Components rendering
describe("The components are rendered", () => { it("renders App component without crashing", () => { shallow(<App />); }); it("renders Form component without crashing", () => { shallow(<Form />); }); it("renders FormList component without crashing", () => { shallow(<FormList />); }); it("renders title without crashing", () => { const wrapper = shallow(<App />); const header = <h1>Become a volunteer</h1>; expect(wrapper.contains(header)).toBe(true); }); it("renders form inputs", () => { const wrapper = shallow(<Form />); expect(wrapper.find('input[name="volunteerName"]')).toHaveLength(1); expect(wrapper.find('input[name="hours"]')).toHaveLength(1); expect(wrapper.find('select[name="jobTitle"]')).toHaveLength(1); }); it("renders submit button without crashing", () => { const wrapper = shallow(<Form />); const label = wrapper.find("#submit-button").text(); expect(label).toBe("Submit"); }); });
Figure 2 – Checking components rendering.
4.2 Passing props
For the first test, the FormList component is mounted and the forms are sent as a property. By using the toEqual() method, you can check if the wrapper received the forms’ props.
The second test goes through each form and using the toHaveProperty() method checks if it received all the properties (id, volunteerName, hours, jobTitle).
import { forms } from "../db.json"; describe("FormList is passing props", () => { it("accepts props", () => { const wrapper = mount(<FormList forms={forms} />); expect(wrapper.props().forms).toEqual(forms); }); it(`should have all properties`, () => { const wrapper = mount(<FormList forms={forms} />); const renderedForms = wrapper.props().forms; for (let i = 0; i < renderedForms.length; i += 1) { expect(renderedForms[i]).toHaveProperty("id"); expect(renderedForms[i]).toHaveProperty("volunteerName"); expect(renderedForms[i]).toHaveProperty("hours"); expect(renderedForms[i]).toHaveProperty("jobTitle"); } }); });
Figure 3 – Checking if the component accepted props.
4.3 Events
On a form, actions can be done using event handlers. Figure 4 shows some examples of how to test the events such as changing the input’s value, submitting the form, and resetting the form. The change event is simulated for all the form inputs.
Following the DRY principle, the logic of simulating the change action of an input was extracted into the simulateOnChangeInput() function. For each input, it’s expected that the new values will be sent to the change method.
After the inputs have been filled with the given values, the click event on the reset button is simulated. The new values that are expected to be received from the inputs are the default ones.
const simulateOnChangeInput = (wrapper, inputSelector, newValue) => { const input = wrapper.find(inputSelector); input.simulate("change", { target: { value: newValue }, }); return wrapper.find(inputSelector); }; describe("The events are working", () => { it("fill the form with values and then reset the form", () => { const wrapper = shallow(<Form />); const updatedNameInput = simulateOnChangeInput( wrapper, "#volunteerName-input", "Endzela Gaye" ); const updatedHourInput = simulateOnChangeInput( wrapper, "#hours-input", "2" ); const updatedJobTitleInput = simulateOnChangeInput( wrapper, "select", "Food supply" ); expect(updatedNameInput.props().value).toBe("Endzela Gaye"); expect(updatedHourInput.props().value).toBe("2"); expect(updatedJobTitleInput.props().value).toBe("Food supply"); wrapper.find("#reset-button").simulate("click"); expect(wrapper.find("#volunteerName-input").props().value).toBe(""); expect(wrapper.find("#hours-input").props().value).toBe(""); expect(wrapper.find("#job-input").props().value).toBe("Raising funds"); }); });
Figure 4 – Filling the form with values and then resetting it
4.4 Mock functions
describe("The events are working", () => { it("The form is submitted when the click event is fired by simulated click on the submit button", () => { const mockCallBack = jest.fn(); const wrapper = shallow(<Form onSubmit={mockCallBack()} />); wrapper.find("#submit-button").simulate("click"); expect(mockCallBack).toHaveBeenCalledTimes(1); }); });
Figure 5 – Submitting the form
5. Snapshot Testing
describe("Snapshot", () => { it("matches App the snapshot", () => { const wrapper = mount(<App />); expect(toJson(wrapper)).toMatchSnapshot(); }); });
Figure 6– Checking if the snapshots are matching
When the tests are run for the first time, a folder named __snapshots__ is created where the snapshot is saved:
Figure 7 – __snapshots__ file
Every time the tests are run, Jest compares the output of the component with the previous Jest snapshot and it will display an error message if there are any differences between them. (Figure 8)
Figure 8 – Differences between the snapshot and the rendered component
A message like this will appear: “1 snapshot failed from 1 test suite. Inspect your code changes or press `u` to update them.”
6. Ensure that your tests are written well!
7. Conclusion
Did you like this article and did you find it helpful? Leave us a message!