Testing with jest & enzyme

Testing react components with jest &Enzyme

react

12-April-2020

Hey folks,

Today I'm going to share my experince on how to test react “components“ specifically with Jest and Enzyme!

Doc's jest and Enzyme

The idea here is how to use it not how to set it up because in the doc's they explained very well, but no problem with some installing points:

1. Install
yarn add -D  jest enzyme enzyme-adapter-react-16 enzyme-to-json
1. Config

Paste this in the bottom of package.json:

"jest": {
    "setupFilesAfterEnv": [
      "<rootDir>config/setupTests.js"
    ],
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ],
    "testEnvironment": "node",
    "collectCoverage": true,
    "moduleNameMapper": {
      "^.+\\.scss$": "identity-obj-proxy"
    },
    "transformIgnorePatterns": [
      "node_modules/(?!(react|jest_enzyme)/)"
    ]
  }

And create file under new directory called config/setupTest.js and paste this in it:

import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";

configure({ adapter: new Adapter() });

now create .babelrcfile and paste this in it:

{
  "presets": ["@babel/react", "@babel/env"],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-runtime"
  ]
}

Install all these dependencies if it prompted you.

Now witn these have done, let's go and create some tests 🤗

Note: Containers should not be tested since the they have just the logic of the app and mainly that they connected with other packages that already tested(ex:redux ...) if you have async calls and you want to test them try to do it component(ex:ComponentDidMount for fetch data...)

Snapshot

Before of all we have to snapshot for the components. This make sure that if the component in the future have been updated or not, this also track any styles with it.

That's simply by:

InputField.test.js

import React from "react";
import { shallow } from "enzyme";

import InputField from "./InputField";

const props = {
  onChange: jest.fn(), // required prop
  value: "anyValue", // required prop
};

describe("InputField-component", () => {
  // Default Snapshot
  test("should match default snapshot", () => {
    expect.assertions(1); // this keep 1 assertions per test

    const wrapper = shallow(<InputField {...props} />);

    expect(wrapper).toMatchSnapshot();
  });
});

This will create snapshot folder with snap of component in it.

You could check your progress testing process in coverage/lcov-report/index.html open it with browser see your progress. Also you could get the points where do you have to cover more.

For optional Props you could create another test step with optional props pass with it.

EventListeners
1. OnChange
import React from "react";
import { shallow } from "enzyme";

import InputField from "./InputField";

const props = {
  onChange: jest.fn(), // required prop
  value: "anyValue", // required prop
};

describe("InputField-component", () => {
  // Trigger onChange
  test("should trigger onChange", () => {
    expect.assertions(1);

    const wrapper = shallow(<InputField {...props} />);
    const value = "something";
    // trigger onChange and save in the state of InputField
    // as a firstName
    wrapper.prop("onChange", {
      target: { value },
    });

    // get the state(firstName)and compared with sent value
    expect(wrapper.state("firstName")).toEqual(value);
  });
});

If you have onChange inside another component use find to locate component:

wrapper.find("InputField").prop("onChange");
// or by id,className and tag
wrapper.find("#firstNameInput").prop("onChange");

expect(props.onChange).toHaveBeenCalled();

If you might to add additional props within onChange props you could validate also:

wrapper.find("InputField").prop("onChange")("any prop");

expect(props.onChange).toHaveBeenCalledWith("any prop");

on pure HTML elements use simulate:

//
wrapper.find("#firstName-input").simulate('change', {...})
2. OnSubmit
wrapper.find("form").simulate("submit", {
  preventDefault: () => null, // or jest.fn()
});
3. OnClick
wrapper.find("button").simulate("click");
// or by props
wrapper.find("Button").prop("click")();
4. Component instances

Let's say that we have componentWillUnMount to remove eventListener from scroll. And we know that the componentDidMount fires first so we could actually unmount the component and do our testing.

wrapper.unmount();

For Other instances you could reach them by call .instance() function:

// this will save 10 elements to state
wrapper.instance().getList();

expect(wrapper.state("list")).toHaveLength(10);

If you have to return value from instance method, it's better to save the returned value and validate it.

const list = wrapper.instance().getList();

expect(list).toHaveLength(10);
Wrapping up

Enzyme makes testing react components so easily and you have a lot of capabilities to work on. This requires you busy hand with it for while and you'll love testing, may be might thinking about TDD 😎.


This blog have been posted also on YouTube Check it out.

Pls don't forget to like, subscribe and share 👏.

Code


Published on CodeReview by Mustafa Alroomi on 12-April-2020