< Previous: Creating our First React Test with Jest   Next: Using Jest to Simulate User Interaction on a React Component >

Using Jest to Test Our React Components

Last chapter we made a trivial test: render the <List> component into a virtual document, and check to make sure it has three <li> elements. I kept it trivial because the goal was to learn how a Jest test looks, so now that's done I want to move onto some real tests:

  1. When the Detail component is rendered, does it have 0 commits?
  2. Is it set to show commits data by default?
  3. When its Forks button is clicked, did it switch to showing forks data?
  4. When its finishes fetching forks from GitHub, does it now have 30 forks to show?

We're going to put all four of those into one test suite, in one file: inside your tests folder please create Detail-test.js. Give it this initial content:

__tests__/Detail-test.js

jest.autoMockOff();

import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';

const Detail = require('../src/pages/Detail').default;

describe('Detail', () => {
    // tests go here
});

Before we write our four tests, we're going to make a tiny change to the Detail component. The GitHub API is great because it provides lots of interesting data without having to register for an API key, but this free access is rate limited, which means you get to make only 60 requests per hour before your access is temporarily paused.

When working on a real application, you would use "mocking" to make a simulated GitHub API request so that a) your tests run faster, and b) you never have to worry about rate limits again. But mocking introduces a new set of complexities that would be a bit overwhelming at this point in your React career, so we're going to insert a hack: we're going to modify the Detail component so that if an empty repo name is passed it skips doing any Ajax calls.

This hack is helpful because it reduces the number of Ajax calls our tests will make, which in turn reduces the likelihood of us hitting GitHub's API limits. Without this hack, each time the Detail component loads it makes three API calls, so across four tests that makes for a total of 12 API calls every time we run our test suite – we'd get through our entire API limit in just five runs!

Let's insert the hack now. Modify the fetchFeed() method in Detail.js to this:

src/pages/Detail.js

fetchFeed(type) {
    if (this.props.params.repo === '') {
        // empty repo name, bail out!
        return;
    }

    const baseURL = 'https://api.github.com/repos/facebook';
    ajax.get(`${baseURL}/${this.props.params.repo}/${type}`)
        .end((error, response) => {
            if (!error && response) {
                this.setState({ [type]: response.body });
            } else {
                console.log(`Error fetching ${type}.`, error);
            }
        }
    );
}

Time for the first test: are there zero commits when the component first loads? This is just a matter of rendering the component then checking that its state.commits property has a length of 0. But this also gives me a chance to show you how to pass a fake path to the Detail component. Add this test in place of the // tests go here comment:

__tests__/Detail-test.js

it('starts with zero commits', () => {
    const rendered = TestUtils.renderIntoDocument(
        <Detail params={{repo: ''}} />
    );

    expect(rendered.state.commits.length).toEqual(0);
});

You've seen how JSX lets us use braces, { }, to mark JavaScript expressions inside markup, but now I'm using double braces, {{ }}. This isn't some special syntax, it's just a combination of { meaning "start a JavaScript expression" then { meaning "start a JavaScript object."

You learned near the beginning of this book that setting attributes on a React component gets translated to props. Well, in this case we're setting a prop called params and giving that a repo property containing an empty string. When our Detail component receives that, it will be able to read this.props.params.repo, which is exactly what it would have received from React Router.

The second test for this component is to check that the component is set to render commits by default. This is very similar to the test we just wrote, so I recommend you give it a try now to make sure you understand how Jest works.

Go on, I'll wait.

Of course, I don't want you to peek at my solution by accident, because then you wouldn't learn anything.

Still here? OK, here's a joke: what's red and smells like blue paint?

Red paint.

I'm going to assume you've either written the test yourself and learned a lot, or you're one of those types who refuses to make a book tell them what to do and you want me to write the solution for you. So, here goes - add this to Detail-test.js beneath the existing test:

__tests__/Detail-test.js

it('shows commits by default', () => {
    const rendered = TestUtils.renderIntoDocument(
        <Detail params={{repo: ''}} />
    );

    expect(rendered.state.mode).toEqual('commits');
});

Easy, right? Right. But that was just to make sure you were paying attention: the next test is harder, because it needs to click the Forks button and make sure the component's state was updated correctly.

Buy the book for $10

Get the complete, unabridged Hacking with React e-book and take your learning to the next level - includes a 45-day no questions asked money back guarantee!

If this was helpful, please take a moment to tell others about Hacking with React by tweeting about it!

< Previous: Creating our First React Test with Jest   Next: Using Jest to Simulate User Interaction on a React Component >

Copyright ©2016 Paul Hudson. Follow me: @twostraws.