Testing tubes in a Lab depicting a test environment like for Jest tests :)

3 Quick Wins to Test Your Presentational React Components with Jest

Did you ever ask yourself, if it makes sense to test presentational components, or if it’s just too time consuming? Good news, you’re not alone! That’s why I put together three ways to create useful Jest tests for your presentational components without spending too much time.

“Does it make sense to test static components that only render a UI?”

The answer is: It depends. It might not be too useful if you only test your JSX, or if  you called componentDidMount correctly. However, there are cases, where you really want to test your component, as well as the one or other quick-win, that can help you to avoid some nasty bugs later on.

In this post we will discuss some ways to introduce basic tests to your app. Please note: This is not meant to be a full-blown in-depth article about the secrets of unit testing and how to meet 100% code coverage. However, some testing is better than no testing – and the kind of tests that we’re discussing in the next sections can save you some hours of debugging and bug fixing – without being too complicated and time-consuming.

Test Conditional Rendering

If your component renders different content or child components, depending on which props you passed, it is a good idea to test, if your component actually renders as intended.

To assert your rendered components with Jest, you can use enzyme or React’s TestUtils. For this example we use enzyme but feel free to use whatever library suits you best.

import React from "react";
import { shallow } from "enzyme";
import ContactListWithLoadingIndicator from "./ContactListWithLoadingIndicator";
import LoadingIndicator from "./LoadingIndicator";
import ContactList from "./ContactList";

const dummyFunc = () => {};
const dummyArray = [
  {
    id: 1,
    firstName: "Max",
    lastName: "Mustermann",
    street: "Duck Creek Road",
    house: 2561,
    zip: 94107,
    city: "San Francisco",
    phone: "650-795-0470",
    email: "max.mustermann@internet.com"
  },
  {
    id: 2,
    firstName: "Maxine",
    lastName: "Musterfrau",
    street: "Duck Creek Road",
    house: 2562,
    zip: 94107,
    city: "San Francisco",
    phone: "650-795-0471",
    email: "maxine.musterfrau@internet.com"
  }
];

test("ContactListWithLoadInd shows LoadingIndicator when loading", () => {
  const contactList = shallow(
    <ContactListWithLoadingIndicator 
      isLoading={true} 
      contacts={dummyArray} />
  );

  const loadingIndicator = contactList.find(LoadingIndicator);

  expect(loadingIndicator).toHaveLength(1);
});

test("ContactListWithLoadInd shows ContactList when not loading", () => {
  const contactList = shallow(
    <ContactListWithLoadingIndicator 
      isLoading={false} 
      contacts={dummyArray} />
    );

  const list = contactList.find(ContactList);
  expect(list).toHaveLength(1);
});

In this example we created two unit tests. The first one, renders our <ContactListWithLoadingIndicator> with isLoading={true} to check, if it renders a <LoadingIndicator>, while in the second test case, we check if it renders the <ContactList> component when it’s not currently loading.

Add Basic Component Tests

“It only renders some UI, it can’t break, so I don’t need to test it”

Imagine the following scenario:  Months after you created your app, you get a change request, that requires you to change a certain object at one point. Chances are, that other components might depend on this object and are now breaking, because of your change. You won’t know if it actually broke something, until you clicked through your whole app. And with “whole app”, I mean each possible combination of components that our app might be able to render. Or …  you could just hope that nothing else depends on the object you changed.

Sounds like a lot of fun, heh?

You can avoid the need of clicking through all possible paths of your app. To do so, we can add basic component tests for each of your components.

To do so, you have to create mock objects for everything you pass down the props of the component. Then you simply render it with ReactDOM inside a Jest test, and if it’s not possible to render for some reason, the test will fail.

import React from "react";
import ReactDOM from "react-dom";
import ContactDetailView from "./ContactDetailView";

const dummyFunc = () => {};
const dummy = {
  id: 1,
  firstName: "Max",
  lastName: "Mustermann",
  street: "Duck Creek Road",
  house: 2561,
  zip: 94107,
  city: "San Francisco",
  phone: "650-795-0470",
  email: "max.mustermann@internet.com"
};

test("ContactDetailView rendered correctly", () => {
  const div = document.createElement("div");
  ReactDOM.render(
    <ContactDetailView
      contact={dummy}
      onDelete={dummyFunc}
      onEdit={dummyFunc}
    />,
    div
  );
});

“But how would I notice that my app breaks from a changed object, if I always pass a correct object in the test?” – You might think

You’re right. The above example renders a component and it’s child components. It only covers errors caused by changes to the component or it’s children. However, if you change the props of a component, you also have to update the tests of this component. So if some child components depend on your changed object through props, this test won’t pass, unless you fixed all the child components. As you can see, this small test might save you some hours bug fixing.

Add Basic Jest Snapshot Tests

Snapshot tests are a powerful tool to exactly compare the rendered markup with a previously saved snapshot.

Let’s have a look how we can create a snapshot for our ContactDetailView

import React from "react";
import ContactDetailView from "./ContactDetailView";
import renderer from "react-test-renderer";

const dummyFunc = () => {};
const dummy = {
 [... cropped for brevity ...] 
};

test("ContactDetailView matches the Snapshot", () => {
  const component = renderer.create(
    <ContactDetailView
      contact={dummy}
      onDelete={dummyFunc}
      onEdit={dummyFunc}
    />
  );

  let tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

As you can see, we first render our component with renderer.create. The first run will create a new snapshot, file that has the exact markup of the rendered component. Now each time we execute the test, it compares the rendered markup with the snapshot.

Snapshots are a good way to do a very detailed check, if something in your components changed. This is especially useful to test presentational components.

However, there are caveats: Each time you change the component, you will have to generate a new snapshot by running jest -u to overwrite the existing snapshot. At this point it’s necessary to manually check how the markup has changed and if it’s really correct. You really don’t want your test to run against an incorrect snapshot. Usually you should check the snapshot before committing the file.

Note: Snapshot tests are worth nothing if you don’t take the time to manually check the changes in your snapshot files.

Wrapping Up

Congratulations to your new skill! You just learned three ways to add basic unit tests to test presentational components for your React components.

  • Testing Conditional Rendering
  • Basic Component Tests
  • Basic Snapshot Testing

As mentioned in the beginning of this post, there are a lot more ways to test your components in detail. However, if you’re concerned about the necessary time effort to add testing to your app, those three approaches are a quick way to cut the risk of adding new bugs. So next time you consider skipping testing to save time, try your favourite approach and cut the risk of adding new bugs!

Do you write unit tests? Why? Why Not? I’d love to hear about it! Leave a comment and tell me about your test experience and how you approach this subject in your projects. 


Improve your React Skills!

Learn about React concepts, helpful libraries or get tips & tricks for deploying your app and many more topics.

I semi regularly post about React. Don't miss out on my future posts! Sign up below to get them delivered directly into your inbox!

Leave a Reply

Your email address will not be published. Required fields are marked *