Crédit photo

Unit testing React components

The React team has implemented a feature called shallow rendering, which

lets you render a component "one level deep" and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.

Sounds good, right? And guess what, shallow rendering is currently the preferred way to test your React components.

As you can see in the post mentioned at the end of this one, the actual code to test some components might seem a bit longer that what you might expect.

Thankfully, someone created something pretty cool: react-element-to-jsx-string. As the name of the package says, this library helps to render a react component into a JSX string.

Now things start to become interesting: with those two things in mind (shallow render and react components as JSX strings), we can easily add some basic unit tests for some components.

There are others techniques to test React components, and most of those involve the DOM. This means you will need to run your tests in the browser (or using jsdom): your tests will be slower than the following method (which is more real unit testing since you execute less code and do not require a huge environment).

#Easy unit testing React components (without a DOM)

Let's do this with the following (dumb) component:

// web_modules/Picture/index.js

import React from "react";
import { PropTypes } from "react";

const Component = ({ img, title, Loader, Title }) => (
  <div>
    {(!img || !img.src) && Loader && <Loader />}
    {img && img.src && <img src={img.src} alt={img.alt} />}
    {title && Title && <Title text={title} />}
  </div>
);

Component.propTypes = {
  img: PropTypes.object,
  title: PropTypes.string,
  Loader: PropTypes.func.isRequired,
  Title: PropTypes.func.isRequired
};

Component.displayName = "Picture";

export default Component;

This component displays an image with a title component. If the image data is not ready yet, it can display a loader component.

Now let's write a simple test for it. For the example we will use tape with the help of tape-jsx-equals, but you will find all kind of flavors on npm.

// web_modules/Picture/__tests__/index.js

import tape from "tape";
import addAssertions from "extend-tape";
import jsxEquals from "tape-jsx-equals";
const test = addAssertions(tape, { jsxEquals });

import React from "react";
import { createRenderer } from "react-addons-test-utils";

import Picture from "..";

// fixtures (empty and stateless react components)
const Loader = () => {};
const Title = () => {};

test("PageContainer is properly rendered", t => {
  const renderer = createRenderer();

  renderer.render(<Picture Loader={Loader} Title={Title} />);
  t.jsxEquals(
    renderer.getRenderOutput(),
    <div>
      <Loader />
    </div>,
    "can render a Loader component if no image data are passed"
  );

  renderer.render(
    <Picture
      Loader={Loader}
      Title={Title}
      img={{
        src: "SRC",
        alt: "ALT"
      }}
    />
  );
  t.jsxEquals(
    renderer.getRenderOutput(),
    <div>
      <img src="SRC" alt="ALT" />
    </div>,
    "should render an image if data are passed"
  );

  renderer.render(
    <Picture
      Loader={Loader}
      Title={Title}
      img={{
        src: "SRC",
        alt: "ALT"
      }}
      title={"TITLE"}
    />
  );
  t.jsxEquals(
    renderer.getRenderOutput(),
    <div>
      <img src="SRC" alt="ALT" />
      <Title text="TITLE" />
    </div>,
    "can render a Title if data are passed"
  );

  t.end();
});

These tests are the minimum coverage to ensure you don't break anything when you work on your component.

As you can see, the tests here are pretty easy to write & straightforward.
The interesting part is that you don't compare using strings. You can use real React components.

You can easily run this full example by getting it from this repository:

github.com/MoOx/react-component-unit-testing-example

This example contains all the commands and dependencies (defined in the package.json) that you might need.

#What about testing events like onClick?

You don't need to reproduce the entire click.

Your tests don't need to check that your onClick prop will be executed when you click on a DOM element. React probably have tests to cover this.

You only need to test that the onClick prop value will do what you want. So if you have something like onClick={ yourCallback }, just call directly yourCallback() in your test just before your comparison. That's enough!

If you want to go deeper, you might also read:

With all those examples, we hope you will stop being afraid to test your code and will not hesitate to cover all your React components with tests 😍.

Written by MoOx

UI developer (web & mobile) with React & friends because he doesn't like HTML & CSS anymore. Open Source addict, handyman and freelancer.

3 contributors on this page