High ROI Testing with React, Enzyme and Jest – Part 2, Writing a Test

By Ryan Vice | Posted on March 12, 2018

React| Testing

Let’s Add Some Code to Test

NOTE: the starting point code for this section is located here: https://github.com/RyanAtViceSoftware/react-testing-best-practices/tree/step-1-create-react-app-setup

We are going to take advantage of JSONplaceholder which is a dummy blog API that’s great for trying out client side code. Let’s update our create-react-app based app to write out a couple of dummy blog post that we will borrow from http://jsonplaceholder.typicode.com/posts. To do this we will update our app.js file as shown below.

Update the App component

Let’s get our App component as shown below.

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      posts: []
    };
  }

  componentDidMount() {
    getPosts()
      .then(posts => this.setState({ posts: posts}));
  }

  render() {
    return (
      <div className="App">
        {!this.state.posts.length && <h3>Loading...</h3>}
        <ul>
          {this.state.posts.map(p => <li key={p.id}>{p.title}</li>)}
        </ul>
      </div>
    );
  }
}

In our App.componentDidmount() method we are on line 11 calling GetPosts , a data access component, to get posts from the server and then in the App.render() method on line 20 we are using use this.state.posts.map() to write the posts returned by getPosts() out to the user.

Let’s add our data access components

To simulate getting data getPosts() method is simply calling our http.get() method and passing the post resources URL, /posts, along with some dummy data to be returned as shown below.

function getPosts() {
  return http.get('/posts', null, { dummyData: getDummyPosts() });
}

In our http.get() method currently we just fakes async calls using setTimeout and we allow for specifying dummyData to be returned in the third parameter parameter as shown below.

export const http = {
  get: (url, config, { dummyData } = {}) => new Promise(resolve =>
    setTimeout(() => {
        resolve(dummyData);
      }, 1000
    )
  )
};

Note: we are making this third parameter a destructed object which allows us to easily add other optional configurations later (like an error to return).

And lastly our getDummyPosts() method simply returns a few posts from http://jsonplaceholder.typicode.com/posts that we copied and pasted in.

function getDummyPosts() {
  return [
    {
      "userId": 1,
      "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    },
    {
      "userId": 1,
      "id": 2,
      "title": "qui est esse",
      "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
    },
    {
      "userId": 1,
      "id": 3,
      "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
      "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
    }
  ];
}

At this point if we run our code using npm start we should see something similar to what is shown below.

Let’s Add Our First New Test

Now let’s open app.test.js and update it as shown below.

import React from 'react';
import ReactDOM from 'react-dom';
import Enzyme, { mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import App from './App';

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

describe('Given we load our app ', () => {
  it('Then renders without crashing', () => {
    const div = document.createElement('div');
    ReactDOM.render(<App />, div);
    ReactDOM.unmountComponentAtNode(div);
  });

  it('Then it shows a loading indicator', () => {
    const app = mount(<App/>);

    expect(app.getElements().length).toBeTruthy();

    const loadingIndicator = app.find('h3');

    expect(loadingIndicator.getElements().length).toBeTruthy();

    expect(loadingIndicator.getElements()[0].props.children).toBe('Loading...');
  });
});

If we save and then run npm test from a console in the root of our repository we should see the output below.

What we have done above is update our test to use Gherkin Syntax and we’ve nested our existing test in a describe block. This makes it so that we are now testing two scenarios and those are clearly describe using Gherkin in our test output and we get some nice nesting by taking advantage of the describe() method from Jest. The two things we are currently testing map nicely to our requirements:

  1. That the app doesn’t blow up when it’s loaded in the DOM
  2. That we are shown a Loading... indicator when the app is first loaded

To accomplish this we added one new test which is shown below.

it('Then it shows a loading indicator', () => {
    const app = mount(<App/>);

    expect(app.getElements().length).toBeTruthy();

    const loadingIndicator = app.find('h3');

    expect(loadingIndicator.getElements().length).toBeTruthy();

    expect(loadingIndicator.getElements()[0].props.children).toBe('Loading...');
  });

What this test method does is first on line 2 mount the App into a JSDOM virtual DOM using Enzyme as shown below.

const app = mount(<App/>);

NOTE:Enzyme is a wrapper around React Test Utils  created by Air BnB because the API for React Test Utils wasn’t the best. They’ve improved the API and now you can easily mount a component like the code below:

To get Enzyme to work we have to first configure Enzyme using the following code:

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

which uses a React 0.16 adapter which is imported with the following code:

import Adapter from 'enzyme-adapter-react-16';

Now that we have our App component mounted in the virtual DOM and we have a ReactWrapper instance in our app variable we can easily query for components using an Enzyme Selector syntax which is very similar to CSS or JQuery selector syntax with a few other goodies. Below we are querying for a descendant of our App component that is an h3 component.

const loadingIndicator = app.find('h3');

This will return a ReactWrapper instance that we can then call the getElements() method on to get an array of ReactWrappers that were returned by our query as shown below.

const loadingIndicator = app.find('h3');

We then verify that we got something back from .find() using the following line.

expect(loadingIndicator.getElements().length).toBeTruthy();

And finally, as shown below. we inspect the props of the first element returned by loadingIndicator.getElements() and verify that the children contains the text Loading... which is what we show when we are loading data from the server.

expect(loadingIndicator.getElements()[0].props.children).toBe('Loading...');

And so now we have written our first test using Enzyme and Jest.

NOTE: the code to this point is located here: https://github.com/RyanAtViceSoftware/react-testing-best-practices/tree/step-3-our-first-test

Continued in part 3: https://vicesoftware.com/react-testing-part-3/

Watch Video Series

5 Keys to Success Before Building Your Software

You may have considered getting a custom software system to help your business run more smoothly, but how do you get started? We will look at 5 factors that we feel are critical to success when building a custom software system.

Need an expert team of designers, mobile developers, web developers and back-end developers?

Discover Vice Software