Tuesday, December 10

Writing Tests for React Applications Using Jest and Enzyme

While it is important to have a well-tested API, solid test coverage is a must for any React application. Tests increase confidence in the code and helps prevent shipping bugs to users.

That’s why we’re going to focus on testing in this post, specifically for React applications. By the end, you’ll be up and running with tests using Jest and Enzyme.

No worries if those names mean nothing to you because that’s where we’re headed right now!

Installing the test dependencies

Jest is a unit testing framework that makes testing React applications pretty darn easy because it works seamlessly with React (because, well, the Facebook team made it, though it is compatible with other JavaScript frameworks). It serves as a test runner that includes an entire library of predefined tests with the ability to mock functions as well.

Enzyme is designed to test components and it’s a great way to write assertions (or scenarios) that simulate actions that confirm the front-end UI is working correctly. In other words, it seeks out components on the front end, interacts with them, and raises a flag if any of the components aren’t working the way it’s told they should.

So, Jest and Enzyme are distinct tools, but they complement each other well.

For our purposes, we will spin up a new React project using create-react-app because it comes with Jest configured right out of the box.

yarn create react-app my-app

We still need to install enzyme and enzyme-adapter-react-16 (that number should be based on whichever version of React version you’re using).

yarn add enzyme enzyme-adapter-react-16 --dev

OK, that creates our project and gets us both Jest and Enzyme in our project in two commands. Next, we need to create a setup file for our tests. We’ll call this file setupTests.js and place it in the src folder of the project.

Here’s what should be in that file:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

This brings in Enzyme and sets up the adapter for running our tests.

To make things easier on us, we are going to write tests for a React application I have already built. Grab a copy of the app over on GitHub.

Taking snapshots of tests

Snapshot testing is used to keep track of changes in the app UI. If you’re wonder whether we’re dealing with literal images of the UI, the answer is no, but snapshots are super useful because they capture the code of a component at a moment in time so we can compare the component in one state versus any other possible states it might take.

The first time a test runs, a snapshot of the component code is composed and saved in a new __snapshots__ folder in the src directory. On test runs, the current UI is compared to the existing. Here’s a snapshot of a successful test of the sample project’s App component.

it("renders correctly", () => {
  const wrapper = shallow(
    <App />
  );
  expect(wrapper).toMatchSnapshot();
});

Every new snapshot that gets generated when the test suite runs will be saved in the __tests__ folder. What’s great about that Jest will check to see if the component matches is then on subsequent times when we run the test, Jest will check to see if the component matches the snapshot on subsequent tests. Here’s how that files looks.

Let’s create a conditions where the test fails. We’ll change the <h2> tag of our component from <h2>Random User</h2> to <h2>CSSTricks Tests</h2> and here’s what we get in the command line when the tests run:

If we want our change to pass the test, we either change the heading to what it was before, or we can update the snapshot file. Jest even provides instructions for how to update the snapshot right from the command line so there’s no need to update the snapshot manually:

Inspect your code changes or press `u` to update them.

So, that’s what we’ll do in this case. We press u to update the snapshot, the test passes, and we move on.

Did you catch the shallow method in our test snapshot? That’s from the Enzyme package and instructs the test to run a single component and nothing else — not even any child components that might be inside it. It’s a nice clean way to isolate code and get better information when debugging and is especially great for simple, non-interactive components.

In addition to shallow, we also have render for snapshot testing. What’s the difference, you ask? While shallow excludes child components when testing a component, render includes them while rendering to static HTML.

There is one more method in the mix to be aware of: mount. This is the most engaging type of test in the bunch because it fully renders components (like shallow and render) and their children (like render) but puts them in the DOM, which means it can fully test any component that interacts with the DOM API as well as any props that are passed to and from it. It’s a comprehensive test for interactivity. It’s also worth noting that, since it does a full mount, we’ll want to make a call to .unmount on the component after the test runs so it doesn’t conflict with other tests.

Testing Component’s Lifecycle Methods

Lifecycle methods are hooks provided by React, which get called at different stages of a component’s lifespan. These methods come in handy when handling things like API calls.
Since they are often used in React components, you can have your test suite cover them to ensure all things work as expected.

We do the fetching of data from the API when the component mounts. We can check if the lifecycle method gets called by making use of jest, which makes it possible for us to mock lifecycle methods used in React applications.

it('calls componentDidMount', () => {
  jest.spyOn(App.prototype, 'componentDidMount')
  const wrapper = shallow(<App />)
  expect(App.prototype.componentDidMount.mock.calls.length).toBe(1)
})

We attach spy to the component’s prototype, and the spy on the componentDidMount() lifecycle method of the component. Next, we assert that the lifecycle method is called once by checking for the call length.

Testing component props

How can you be sure that props from one component are being passed to another? We have a test confirm it, of course! The Enzyme API allows us to create a “mock” function so tests can simulate props being passed between components.

Let’s say we are passing user props from the main App component into a Profile component. In other words, we want the App to inform the Profile with details about user information to render a profile for that user.

First, let’s mock the user props:

const user = {
  name: 'John Doe',
  email: 'johndoe@gmail.com',
  username: 'johndoe',
  image: null
}

Mock functions look a lot like other tests in that they’re wrapped around the components. However, we’re using an additional describe layer that takes the component being tested, then allows us to proceed by telling the test the expected props and values that we expect to be passed.

describe ('<Profile />', () => {
  it ('contains h4', () => {
    const wrapper = mount(<Profile user={user} />)
    const value = wrapper.find('h4').text()
    expect(value).toEqual('John Doe')
  })
  it ('accepts user props', () => {
    const wrapper = mount(<Profile user={user} />);
    expect(wrapper.props().user).toEqual(user)
  })
})

This particular example contains two tests. In the first test, we pass the user props to the mounted Profile component. Then, we check to see if we can find a <h4> element that corresponds to what we have in the Profile component.

In the second test, we want to check if the props we passed to the mounted component equals the mock props we created above. Note that even though we are destructing the props in the Profile component, it does not affect the test.

Mock API calls

There’s a part in the project we’ve been using where an API call is made to fetch a list of users. And guess what? We can test that API call, too!

The slightly tricky thing about testing API calls is that we don’t actually want to hit the API. Some APIs have call limits or even costs for making making calls, so we want to avoid that. Thankfully, we can use Jest to mock axios requests. See this post for a more thorough walkthrough of using axios to make API calls.

First, we’ll create a new folder called __mock__ in the same directory where our __tests__ folder lives. This is where our mock request files will be created when the tests run.

module.exports = {
  get: jest.fn(() => {
    return Promise.resolve({
    data: [
      {
        id: 1,
        name: 'Jane Doe',
        email: 'janedoe@gmail.com',
        username: 'jdoe'
      }
    ]
    })
  })
}

We want to check and see that the GET request is made. We’ll import axios for that:

import axios from 'axios';

Just below the import statements, we need Jest to replace axios with our mock, so we add this:

jest.mock('axios')

The Jest API has a spyOn() method that takes an accessType? argument that can be used to check whether we are able to “get” data from an API call. We use jest.spyOn() to call the spied method, which we implemented in our __mock__ file, and it can be used with the shallow, render and mount tests we covered earlier.

it('fetches a list of users', () => {
  const getSpy = jest.spyOn(axios, 'get')
  const wrapper = shallow(
    <App />
  )
  expect(getSpy).toBeCalled()
})

We passed the test!

That’s a primer into the world of testing in a React application. Hopefully you now see the value that testing adds to a project and how relatively easy it can be to implement, thanks to the heavy lifting done by the joint powers of Jest and Enzyme.

Further reading

Source: CSS-tricks.com

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x