Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Test React Components With Jest and Enzyme, Part 1

DZone 's Guide to

How to Test React Components With Jest and Enzyme, Part 1

In Part 1, we look at how to get set up using Jest and Enzyme for testing the components of React-based applications. Let's get to it!

· Web Dev Zone ·
Free Resource

Testing React components may be challenging for beginners and experienced developers who have already worked with tests. It may be interesting to compare your own approaches with the ones we use in our project. In order to cover the codebase, you have to know which components must be tested and which code exactly in a component should be covered.

During reading, I’ll cover the next topics:

  • Define the correct order of components’ testing based on project structure.
  • Find what to omit in test coverage (what not to test).
  • Identify the necessity of Snapshot Testing.
  • Define what to test in the component and in which order.
  • Provide detailed custom code examples.

The article requires that the reader already has knowledge of Jest and Enzyme. Information about installation and configuration can be easily found on the web or on official websites.

Assume the following: You need to cover the project codebase with tests. So what should you start with and what should you get at the end of testing? 100% test coverage? It is the indicator to which you should aspire, but in most situations, you won’t get it. Why? Because you shouldn’t test all the code. We will find out why and what should be left out of tests. Even more, 100% test coverage does not always ensure that the component is fully tested. As well, there is no guarantee it will notify you if something has been changed. Don’t strive for the percentages, avoid writing fake tests, and just try not to lose main component details.

Defining the Correct Order of Components Testing Based on Project Structure

Let’s discuss this question on the next part of the project structure:

defining-queue-for-project-structure

I took the shared directory because it is the most important; it consists of the components that are used in several different pages of the project. They are reusable and, normally, they are small and not complex. If one or another component fails, it will cause failure in other places. That’s why we should be confident taht they have been written correctly. The structure of this directory is divided into several folders, each containing components.

project-structure

How to define the correct order of component testing in shared directory:

  • Always follow the rule from simple to complex. Analyze each directory and define which components are independent — namely, their rendering doesn’t depend on the other components; they are self-completed and can be used separately as a single unit. From the structure above, it is the inputsdirectory in forms folder. It contains input components to redux-forms, such as TextInput, SelectInput, CheckboxInput, DateInput, etc.
  • Next, I need to define auxiliary components that are often used in inputs components, but should be tested apart from them. It is utils directory. Components in this folder are not complicated, but very important. They are frequently reusable and help with repeated actions.
  • The next step is to define which components can be used independently too. If any, take them for testing. From our structure, it is widgets, the little components with simple functionality. They will be the third item in the queue for test coverage.
  • Further, analyze the rest of the directories and define more complex components, which can be used independently or in conjunction with other components. It is modals directory in our case; these components will be explained in detail below.
  • The most complex for testing components are left in the end. They are hoc directory and fieldsfrom forms folder. How do you define which one should be tested first? I take the directory from which components have already been used in tested components. Thus, component from hocdirectory was present in widgets component; that’s why I already know where and with which purpose this directory and its component are used.
  • The last one is the fields folder; it contains components connected with redux-forms.

The final components order (based on our example) will look like this:

components-order

Following this order, you increase the complexity of the tested components step by step; thus, when it comes to operating with the more complex components, you already know how the smallest ones behave. Don’t take the ‘array’ field for testing, for example, if you are not sure how to test the ‘text’ field; don’t take components decorated with redux-forms if you haven’t tested the ‘form’ field itself. Be consistent in your choices, don’t take the first component that comes into your mind, and switch on logic. Of course, the structure of your project can differ; it can have other directory names or can have additional components, actions, and reducers, but the logic of defining the order for testing the components is the same.

Let’s define what should be omitted in test coverage:

  1. Third-party libraries. Don’t test functionality that is taken from another library; you are not responsible for that code. Skip it or imitate implementation if you need it to test your code.
  2. Constants. The name speaks for itself. They are not changeable; it is a set of static code that is not intended to vary.
  3. Inline styles (if you use them as a component). In order to test inline styles, you need to duplicate object with styles in your test; if the styles object changes, you must change it in the test too. Don’t duplicate a component’s code in tests. In most cases, inline styles don’t change a component’s behavior; consequently, they shouldn’t be tested. There can be an exception in case your styles change dynamically.
  4. Things not related to the tested component. Skip covering with tests components that were imported into the tested component; be careful if it is wrapped in another one. Don’t test wrappers, just analyze and test them separately.

So how do you actually write tests? I combine two testing approaches:

  • Snapshot testing.
  • Component logic testing.

Snapshot testing is a useful testing tool in case you want to be sure the user interface hasn’t changed. When facing this testing tool for the first time, questions arise concerning organization and managing snapshots. The principle of work is very simple, but unfortunately, it has not been fully described anywhere; on the official website, jestjs.io, the description of Snapshot Testing work is very poor.

How to Test With Snapshots

Step 1. Write a test for the component and, in expect, use the .toMatchSnapshot() method that creates the Snapshot itself.

it('render correctly text component', () => {  
    const TextInputComponent = renderer.create(<TextInput />).toJSON();
    expect(TextInputComponent).toMatchSnapshot();
});

Step 2. When you run a test for the first time on the one level, along with the test, there will be a directory created, named __snapshots__, with the autogenerated file inside with the extension.snap.

A Snapshot looks like:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Render TextInput correctly component 1`] = `  
<input  
  className="input-custom"
  disabled={undefined}
  id={undefined}
  name={undefined}
  onBlur={undefined}
  onChange={[Function]}
  pattern={undefined}
  placeholder={undefined}
  readOnly={false}
  required={undefined}
  type="text"
  value={undefined}
/>
`;

Step 3. Push the snapshot into the repository and store it along with the test.

If the component has been changed, you just need to update Snapshot with the —updateSnapshot flag or by using the short-form u flag.

The Snapshot Is Created, but How Does it Work?

Let us consider two cases:

1. The Component Has Changed

  • Run tests.
  • A new snapshot is created and it compares with the auto-generated snapshot stored in the directory __snapshots__
  • Tests failed because a snapshot is different.

test-failed

2. The Component Has Not Changed

  • Run tests.
  • A new Snapshot is created, which compares the tests with the auto-generated Snapshot stored in the directory __snapshots__
  • The tests passed because the Snapshots are identical.

test-succes

Everything is fine when I test a small component without logic, just UI rendering, but, as practice shows, there are no such components on real projects. If they are, they are in small amount.

Is there enough to run a  full component test?

Main Instructions for Component Testing

1. One component should have only one snapshot. If one snapshot fails, most likely the others will fail too, so do not create and store a bunch of unnecessary snapshots clogging up space and confusing developers who will read your tests after you. Of course, there are exceptions when you need to test the behavior of a component in two states; for example, in the state of the component before opening the pop-up and after opening. However, even such variants can always be replaced by this one: the first test stores default state of the component without popups in a snapshot, and the second test simulates events and checks for the presence of a particular class. In this way, you can easily bypass the creation of several snapshots.

2. Testing props: As a rule, I divide the testing of the props into two tests. Firstly, check the render of default prop values; when the component is rendered, I expect a value to be equal from defaultPropsin case this prop has defaultProps. Secondly, check the custom value of the prop; I set my own value and expect it to be received after the render of the component.

3. Testing data types: In order to test what type of data comes in the props or what kind of data is obtained after certain actions, I use the special library jest-extended (additional Jest matchers), which has an extended set of matches that are absent in Jest. With this library, the testing of data types is much easier and more enjoyable. Testing prototypes is a contradictory question. Some developers can argue against prototype testing because it is a third-party package and shouldn’t be tested, but I insist on testing components’ prototypes because I don’t test the package functionality itself; I just ensure the prototypes are correct. The data type is a very important programming concept and shouldn’t be skipped.

4. Event testing: After creating a snapshot and covering props with tests, you can be sure of an incorrect rendering of the component, but this is not enough for full coverage in case you have events in the component. You can check the event in several ways; the most widely used are:

  • mock event => simulate it => expect event was called
  • mock event => simulate event with params => expect event was called with passed params
  • pass necessary props => render component => simulate event => expect a certain behavior on called events

5. Testing conditions: Very often you can have conditions for the output of a particular class, rendering a certain section of the code, transferring the required props, and so on. Do not forget about this because, with default values, only one branch will pass the test, while the second one will remain untested. In complex components with calculations and lots of conditions, you can miss some branches. To make sure all parts of the code are covered by tests, use test coverage tools and visually check which branches are covered and which are not.

6. States’ testing: To check state, in most cases, it is necessary to write two tests:

  • The first one checks the current state.
  • The second one checks state after calling an event. Render the component => call function directly in the test => check how the state has changed. To call a function of the component, you need to get an instance of the component and only then call its methods (an example is shown on GitHub, here).

After you walk through this list of instructions, your component will be covered from 90 to 100%. I leave 10% for special cases that were not described in the article but can occur in the code.

That's all for Part 1. Tune back in tomorrow when we'll dive into some code! 

Topics:
jest ,web dev ,react.js ,front-end development ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}