Comparing React testing libraries
Tags: react, testing
Published: 2020-01-08 17:52:59

---
title: Comparing React testing libraries
published: true
date: 2020-01-02 14:00:55 UTC
tags: react,testing
canonical_url: https://blog.logrocket.com/comparing-react-testing-libraries/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2fyvjn50hkfmiqlywm4p.png
---
**Written by [Murat Çatal](https://blog.logrocket.com/author/muratcatal/)**✏️

If you create something, no matter what it is, you should test it before serving it to other people. That way, you can have more confidence and control over your finished product before you roll it out.

## Application testing strategies

There are several ways to test applications, everything from little pieces of code blocks to more general aspects. Before we dive into the frameworks and libraries associated with them, let’s examine some of the most useful methods to evaluate the functionality of your app.

### Unit test

A [unit test](https://dev.to/bnevilleoneill/the-best-unit-testing-frameworks-for-node-js-f0g) examines each small piece of your code. You may think of it as testing primitive components in their life cycles. This is often the simplest and least expensive testing option.

### Integration test

If you have lots of composed components, you may want to test how they interact with each other. You can do this by mocking your endpoints as part of an integration test. This can be costlier and more complex than unit testing.

### End-to-end test

When it comes time to test the whole system with real data to see whether everything works as expected, end-to-end testing is your best bet.

When you start writing your test, you may be tempted to tinker with your component’s internal business and test implementation details, which will lead you down the wrong path. Instead, you should write tests from the user’s perspective to generate cleaner and more accurate test cases. After all, your end users are not interested in your component’s internal details, but they are interested in what they see.

Now that we’ve established some general best practices, let’s take a closer look at some of the most common testing frameworks and runners. We’ll examine the learning curves, capabilities, and pros and cons associated with each.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Jest

[Jest](https://blog.logrocket.com/testing-with-jest-from-zero-to-hero-85ce0e9cc953/) is a testing framework created and maintained by Facebook. If you build your React application with `create-react-app`, you can start using Jest with zero config. Just add `react-test-renderer` and `@testing-library/react` library to conduct snapshot and DOM testing.

With Jest, you can:

- Conduct snapshot, parallelization, and async method tests
- Mock your functions, including third-party `node_module` libraries
- Execute myriad [assertion methods](https://jestjs.io/docs/en/expect)
- View code coverage report

Now let’s get our hands dirty with some code.

### Installation

Let’s assume your application is created via `create-react-app`.

```jsx
// For snapshot test
yarn add -D react-test-renderer

// For DOM test
yarn add -D @testing-library/react
```

For an existing application that is not built with `create-react-app`, follow these steps:

1. Add dependencies.
```jsx
yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
```
2. Configure your babel.
```jsx
// babel.config.js
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
};
```
3. Add the testing command in your `package.json`.
```jsx
// package.json
{
"scripts": {
"test": "jest"
}
}
```

### Testing structure

Now that you’ve added test files to your application, let’s dive into some more details about the testing structure.

As shown below, `create-react-app` has been configured to run tests that have `.spec.js` and `.test.js` files.

```jsx
// MyComponent
export const MyComponent = ({ label }) =&gt; {
  return &lt;div&gt;{label}&lt;/div&gt;;
};
```

We have a [simple component](https://codesandbox.io/s/testing-with-jest-w1si4) that takes a label prop and displays it on the screen. The next step is to write a small test to ensure that it displays properly.

```jsx
import React from "react";
import { cleanup, render } from "@testing-library/react";
import { MyComponent } from "./MyComponent";

afterEach(cleanup);

describe("MyCompnent", () =&gt; {
  test("should display label", () =&gt; {
    const { getByText } = render(&lt;MyComponent label="Test" /&gt;);
    expect(getByText("Test")).toBeTruthy();
  });
});
```

Now let’s go over the features we want to test.

#### `afterAll` and `beforeAll`

Run functions after the tests are completed in the current test file or before your test starts. You can clean up your resources and mock data created on the database by using the `afterAll` function, or you can set up your configurations in `beforeAll`.

That function may return a generator or a promise, and it will wait for your promise or generator function to finish its execution before it continues.

```jsx
// MyTestFile.test.js
afterAll(() =&gt; {
  cleanResources();
});

beforeAll(() =&gt; {
   setupMyConfig();
});

describe("MyComponent",() =&gt; {
   test("should do this..",() =&gt; {
      expect(prop).toBeTruthy();
   });
});
```

`afterAll` runs when all your tests finish their executions in the current file.

#### `afterEach` and `beforeEach`

Unlike `afterAll` and `beforeAll`, these functions are called for each test case in your test file. By using `beforeEach`, you can create a connection to your database before each test case start to run. As a best practice, you should use `afterAll` to remove your created DOM elements after each test case run.

```jsx
// MyTestFile.test.js
afterAll(() =&gt; {
  resetDomTree();
});

beforeAll(() =&gt; {
  createDomElement();
});

describe("MyComponent",() =&gt; {
   test("should do this..",() =&gt; {
      expect(prop).toBeTruthy();
   });

   test("should do that..",() =&gt; {
      expect(prop).toBeTruthy();
   });
});
```

#### `describe`

This command allows you to group related tests to produce a cleaner output.

```jsx
describe("MyComponent",() =&gt; {
   test("should do this..",() =&gt; {
      expect(prop).toBeTruthy();
   });

   test("should do that..",() =&gt; {
      expect(prop).toBeTruthy();
   });
});
```

#### Snapshot testing

A [snapshot test](https://blog.logrocket.com/refactoring-legacy-code-with-jest-snapshots-e290ceccccc3/) generates an HTML-like output so you can see how your component is structured. It’s especially useful if you want to see how your CSS properties are injected according to events.

```jsx
import React from 'react';
import Link from '../Link.react';
import renderer from 'react-test-renderer';

test('renders correctly', () =&gt; {
  const tree = renderer
    .create(&lt;Link page="http://www.mydomain.com"&gt;My Domain&lt;/Link&gt;)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

// generated snapshot
exports[`renders correctly 1`] = `
&lt;a
  className="normal"
  href="http://www.mydomain.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
&gt;
  My Domain
&lt;/a&gt;
`;
```

#### Mocking functions

Mocking while testing is one of the core features you will need to implement. Jest is great for mocking not only your functions but also your modules.

For example, let’s say you want to test a function that fetches users. It uses `axios`, but we don’t want to hit a real endpoint because that’s not what we want to test.

```jsx
import axios from 'axios';
import { Customers } from "./customers";

jest.mock('axios');

test('should fetch users', () =&gt; {
  const customers = [{name: 'Bob'}, {name: 'Jenny'}];
  const resp = {data: customers.find(c =&gt; c.name = 'Bob')};
  axios.get.mockResolvedValue(resp);

  return Customers.getByFilter("Bob").then(data =&gt; expect(data).toEqual({name: 'Bob'}));
});
```

## Jasmine

Like Jest, Jasmine is a JavaScript framework and test runner. However, you should add some configuration before you start using Jasmine.

On the pro side, here are some things you can do with Jasmine:

- Async function tests
- Mocking requests
- Custom equality checker assertion
- Custom matcher assertion

As for drawbacks, below are some things Jasmine does not support:

- Snapshot tests
- Code coverage tools
- Parallelization (requires third-party tools)
- Native DOM manipulation (requires a third-party tool such as JSDOM)

In addition, Jasmine looks for only `.spec.js` files; you must edit its configuration to look for `.test.js` files too.

### Installation

Jasmine is mostly used with Enzyme, so you will need to install it and make some configurations.

```jsx
yarn add -D babel-cli \
            @babel/register \
            babel-preset-react-app \
            cross-env \
            enzyme \
            enzyme-adapter-react-16 \
            jasmine-enzyme \
            jsdom \
            jasmine
```

Initialize your project for Jasmine with the following command.

```jsx
yarn run jasmine init
```

Now we’ll put some configuration files in a spec/helper folder. They will be for Babel, Enzyme, and JSDOM.

```jsx
// babel.js
require('@babel/register');

// for typescript
require('@babel/register')({
    "extensions": [".js", ".jsx", ".ts", ".tsx"]
});


// enzyme.js or enzyme.ts 
// be sure your file extension is .ts if your project is a typescript project
import jasmineEnzyme from 'jasmine-enzyme';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

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

beforeEach(function() {
  jasmineEnzyme();
});


// jsdom.js

import {JSDOM} from 'jsdom';

const dom = new JSDOM('&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;');
global.document = dom.window.document;
global.window = dom.window;
global.navigator = dom.window.navigator;
```

Lastly, edit the Jasmine configuration file to ensure that the Babel, Enzyme, and JSDOM configs are loaded correctly.

Now it’s time to move into `spec/suppor/jasmine.json`.

```jsx
// the important part here is we should load babel firstly.

// for normal projects
"helpers": [
  "helpers/babel.js",
  "helpers/**/*.js"
],

// for typescript projects
"helpers": [
  "helpers/babel.js",
  "helpers/**/*.{js,ts}"
],
```

Let’s review how we write a test with Jasmine. We will also touch upon Enzyme.

Most of the helper functions, such as `afterAll`, `beforeAll`, `afterEach`, and `beforeEach`, are similar to Jest, so let’s dig into how to write a basic test for a React component to see its structure.

```jsx
const Utils = React.addons.TestUtils;
let element;
beforeEach(() =&gt; {
  element = React.createElement(
      MyComponent,
      {
        label: 'Hello'
      });
});

afterEach(() =&gt; {
  element = null;
});

describe('MyComponent', function() {
  it('can render without error', function() {
    const component = Utils.renderIntoDocument(element);
    expect(component).not.toThrow();
  });
})
```

### Custom matcher

In Jasmine, you can write custom matcher functions to reuse globally in each test spec. A custom matcher could come in handy if, for example, you have a specified group of testing matchers that are used frequently.

Custom matchers should return an object that has `pass` and `message` properties. A `pass` property checks that conditions are in a valid state. `message` is the field that is shown on a failed state.

```jsx
const customMatchers = {
  toBeValidAgeRange: function() {
    return {
      compare: function(actual, expected) {
         var result = {};
         result.pass = (actual > 18 && actual <=35);
         result.message = actual + ' is not valid';   
         return result;
      }
    };
  }
};


describe("Custom matcher", function() {
  beforeEach(function() {
    // register our custom matcher
    jasmine.addMatchers(customMatchers);
  });
  it("should be valid age", function() {
    expect(19).toBeValidAgeRange();
  });

  it("should fail", function() {
    expect(38).toBeValidAgeRange();
  });
});
```

### Custom equality checker

Sometimes, you may need to compare two objects or change the behavior of equality checking to compare primitive types. Jasmine has a good API for overriding equality checking.

Custom checker function must have two parameters: the first comes from `expect` and the second comes from the `assertion` function. Also, it must return `boolean` or `undefined`. If it returns `undefined`, that means the equality function is not suitable for these parameters.

```jsx
function myObjectChecker(first, second) {
  if (typeof first === 'object' && typeof second === 'object' && 
      first.hasOwnProperty('name') && second.hasOwnProperty('name')) {
    return first.name === second.name;
  }
}

beforeEach(() => {
  jasmine.addCustomEqualityTester(myObjectChecker);
});

describe('MyComponent', function() {
  it('can render without error', function() {
    expect({name: 'John'}).toEqual({name:'John'});
  });
});
```

## `react-testing-library`

Created by Kent C. Dodds and maintained by a huge community of developers, this library enables you to test components without touching their internal business — which in turn empowers you to conduct more powerful test cases while keeping the user experience top of mind.

With `react-testing-library`, you can:

- Query your elements within text, `label`, `displayValue`, `role`, and `testId`
- Fire any event
- Wait for an element to appear with `wait`

However, you cannot:

- Conduct shallow rendering
- Access internal business of your components, such as states

### Installation

```jsx
yarn add -D @testing-library/react
```

Now for the fun part…

```jsx
import React from 'react';
import { render, RenderOptions, RenderResult } from '@testing-library/react';

describe('MyComponent', () =&gt; {
  test('should label be in document', () =&gt; {
    const {container, util} = render(&lt;MyComponent label='Hello' /&gt;);
    const label = utils.getByText('Hello');
    expect(label).toBeInTheDocument();
  });
}
```

_(_[_API documentation_](https://testing-library.com/docs/dom-testing-library/api-queries)_)_

## Enzyme

[Enzyme](https://blog.logrocket.com/getting-started-with-enzyme-for-react-a106b58fc53b/) is a JavaScript testing utility framework designed to help developers test React components easily. It’s maintained by Airbnb and is among the most-used frameworks.

Enzyme enables you to:

- Use shallow rendering
- Access business implementations of your components
- Conduct full DOM rendering
- Use `react-hooks` in shallow rendering, with some limitations

### Installation

```jsx
yarn add -D enzyme enzyme-adapter-react-16
```

Create an `enzyme.js` in `src` folder, as shown below.

```jsx
import Enzyme, { configure, shallow, mount, render } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });
export { shallow, mount, render };
export default Enzyme;
```

Now let’s do some coding.

### Shallow rendering

```jsx
import React from 'react';
// we are importing from our enzyme.js
import { shallow } from './enzyme';

import MyComponent from './MyComponent';

describe('MyComponent', () =&gt; {
  test('renders correct text in item', () =&gt; {
    const wrapper = shallow(&lt;MyComponent label="Hello" /&gt;);

    //Expect the child of the first item to be an array
    expect(wrapper.find('.my-label').get(0).props.children).toEqual('Hello');
  });
});
```

### Full DOM rendering

```jsx
describe('&lt;Foo /&gt;', () =&gt; {
  it('calls componentDidMount', () =&gt; {
    sinon.spy(Foo.prototype, 'componentDidMount');
    const wrapper = mount(&lt;Foo /&gt;);
    expect(Foo.prototype.componentDidMount).to.have.property('callCount', 1);
  });
}
```

Beware of `componentDidMount`! We accessed internal business of our component, which may lead you to write incorrect test cases if you’re not careful.

## End-to-end tests

Up to this point, we’ve examined testing libraries in terms of writing unit or integration tests. However, we may also need a fully integrated test with the backend before going to production. For that purpose, we will look to two libraries: Cypress and Puppeteer.

### Cypress

[Cypress](https://dev.to/bnevilleoneill/how-to-write-useful-end-to-end-tests-with-cypress-mmk) enables you to write your tests without any additional testing framework. It has a nice API to interact with page components, and it supports Chrome/Chromium, Canary, and Electron.

What you can do;

- Time travel
- Screenshots and videos
- Automatic waiting
- Control network traffic without touching your server to test edge cases
- Parallelization

Use the following lines of code to install and run Cypress, respectively.

```jsx
yarn add -D cypress
yarn run cypress open
```

Now let’s write some tests.

First, create a file named `my-test_spec.js`.

```jsx
describe('My First Test', function() {
  it('Gets, types and asserts', function() {
    cy.visit('https://www.mydomain.com')
    cy.contains('login').click()

    cy.url().should('include', '/login')

    cy.get('.email')
      .type('my@email.com')
      .should('have.value', 'my@email.com')
  })
})
```

### Puppeteer

[Puppeteer](https://blog.logrocket.com/end-to-end-testing-react-apps-with-puppeteer-and-jest-ce2f414b4fd7/) is not a JavaScript testing framework — it’s a headless Chromium library. You can start your Chromium and, with the provided API, navigate between pages, get buttons, and click on them.

Puppeteer runs on a real browser and enables you to write your end-to-end tests with an API similar to the browser.

To install, enter the following line of code.

```jsx
yarn add -D jest-puppeteer puppeteer jest
```

Then enter the following in `package.json`.

```jsx
// package.json
{
 jest: {
    "preset": "jest-puppeteer"
  }
}
```

Below is the code for our e2e testing.

```jsx
beforeAll(async ()=&gt; {
  await page.goTo('http://mydomain.com');
});

describe('Visit MyDomain', () =&gt; {
  test('should have login text', () =&gt; {
     await expect(page).toMatch('login');
  });
});
```

## Comparing React testing libraries and frameworks head to head

Until now, we looked at features of libraries and how to implement them in our projects. Now, let’s examine some benchmarks and compare the results among libraries.

### Jest versus Jasmine

As we mentioned in the beginning, Jest and Jasmine are used as testing frameworks. You group your test cases within describe blocks and write your tests within `test` or `it` functions.

Now let's break down our comparison in a handy, easy-to-read table.

![Comparing React Testing Frameworks: Jest Versus Jasmine](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/12/jest-jasmine-comparison.png?resize=730%2C513&ssl=1)

Here’s what I like most about Jest:

- Zero configuration required
- Snapshot testing support
- Code coverage support
- Mocking functions

As for Jasmine, the most useful feature is its mocking function. Though this is somewhat limited, it is sufficient for most use cases.

I am currently using Jest in a product due to its native support within the React community, and because it serves our needs in terms of testing React components more so than Jasmine.

### `react-testing-library` versus Enzyme

Among the most important considerations when writing tests for a component are your `util` functions. They may force you to write a cleaner and truer way of testing or lead you to write your tests incorrectly in terms of exported APIs.

![Comparing React Testing Libraries: react-testing-library Versus Enzyme](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/12/react-testing-library-enzyme-comparison.png?resize=730%2C301&ssl=1)

When writing tests for your components, don’t get too bogged down in the implementation details. Remember, try to think about it from the user’s perspective. This will help you produce better test suites, which will help you feel more confident about your tests.

For most use cases, I prefer `react-testing-library`, primarily because its exported APIs do not allow you to use a component’s internal API, which forces you to write better tests. In addition, there is zero configuration required.

Enzyme, on the other hand, lets you use a component’s internal API, which can include life cycle methods or state.

I’ve used both enzyme and `react-testing-libraries` in many projects. However, I’ve often found that `react-testing-library` makes things easier.

### Cypress versus Puppeteer

Testing your critical pages end to end may save your life before going to production. Below is a summary comparison of Cypress and Puppeteer.

![Comparing React Testing Libraries: Cypress Versus Puppeteer](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2019/12/cypress-puppeteer-comparison.png?resize=730%2C281&ssl=1)

Since Cypress is a testing framework, it has many advantages over Puppeteer when the things you want to develop need to be fast. Its APIs are developer-friendly and it enables you to write a test like you would write a unit test. Puppeteer is not a testing framework but a browser. Its APIs are not developer-friendly, but they are powerful because you can access the browser’s API. Thus it comes with a steeper learning curve than Cypress.

## Conclusion

As you can see, each testing method, library, and framework brings its own advantages and shortfalls, depending on the use case and types of data you wish to analyze. After evaluating each testing framework with these factors in mind, it’s clear that `react-testing-library` is the most valuable and logical choice for unit and integration tests. For end-to-end testing, Cypress is an appropriate choice for its easy-to-learn API.

* * * 
## Full visibility into production React apps 

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket.](https://www2.logrocket.com/react-performance-monitoring)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/eq752g8qhbffxt3hp9t4.png)

[LogRocket](https://www2.logrocket.com/react-performance-monitoring) is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — [start monitoring for free.](https://www2.logrocket.com/react-performance-monitoring)
* * *

The post [Comparing React testing libraries](https://blog.logrocket.com/comparing-react-testing-libraries/) appeared first on [LogRocket Blog](https://blog.logrocket.com).