Test React Component with cypress-react-unit-test Example
Tags: react, testing
Published: 2020-05-26 23:55:37

---
title: Test React Component with cypress-react-unit-test Example
published: true
description: How to test a React component and see what is going on
tags: react, testing
---

I have read [Testing React Components with react-test-renderer and the Act API](https://www.valentinog.com/blog/testing-react/) by [Valentino Gagliardi](https://twitter.com/gagliardi_vale) and thought it was a great post. I wanted to see how the same tests could be written using [Cypress](https://github.com/cypress-io/cypress) and [cypress-react-unit-test](https://github.com/bahmutov/cypress-react-unit-test). You can find my source code in repo [bahmutov/testing-react-example](https://github.com/bahmutov/testing-react-example)

Let's get a React component working in the repository. The simplest case is to use [react-scripts](https://www.npmjs.com/package/react-scripts).

```shell
# We need react-scripts to build and run React components
npm i -S react react-dom react-scripts
# We need Cypress test runner and
# React framework adaptor
npm i -D cypress cypress-react-unit-test
```

## Button component

Let's test the `Button` component in the `src` folder. Let's write the spec first, and we can code the `Button` component directly inside the spec file before factoring it out into its own file.

```text
testing-react-example/
  cypress/
    fixtures/
    integration/
    plugins/
    support/
  src/
    Button.spec.js
  package.json
  cypress.json
```

The `cypress.json` file has all Cypress settings. In our case we want to enable the [experimental component testing](https://glebbahmutov.com/blog/my-vision-for-component-tests/) feature.

```json
{
  "experimentalComponentTesting": true,
  "componentFolder": "src",
  "specFiles": "*spec.*"
}
```

The `src/Button.spec.js` looks like this:

```js
/// <reference types="cypress" />
import React from 'react'
import { mount } from 'cypress-react-unit-test'

function Button(props) {
  return <button>Nothing to do for now</button>;
}

describe("Button component", () => {
  it("Matches the snapshot", () => {
    mount(<Button />);
  });
});
```

We run this test in interactive mode with command

```shell
npx cypress open
```

and clicking `Button.spec.js` filename.

The test passes - and at first it does not look like much.

![Button spec is passing](https://dev-to-uploads.s3.amazonaws.com/i/6nydv5u5ft72hi63qrdi.png)

Look closer - this is real browser (Electron, Chrome, Edge or Firefox) running the `Button` component as a mini web application. You can open DevTools and inspect the DOM just like you would with a real web application - because it is real.

![Opening DevTools and inspecting the Button](https://dev-to-uploads.s3.amazonaws.com/i/ddjpsjo16kwzz7b369c7.gif)

## Button with state

Now that we have the component and a corresponding component test, let's make the component a little more interesting.

```js
import React from "react";
import { mount } from "cypress-react-unit-test";

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: "" };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(() => {
      return { text: "PROCEED TO CHECKOUT" };
    });
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.text || this.props.text}
      </button>
    );
  }
}

describe("Button component", () => {
  it("it shows the expected text when clicked (testing the wrong way!)", () => {
    mount(<Button text="SUBSCRIBE TO BASIC" />);
    cy.get('@Button')
  });
});
```

Hmm, how do we check the state value of the component? We don't! The state is an internal implementation detail of the component. Instead we want to test the component using events from the user, like Click.

```js
describe("Button component", () => {
  it("it shows the expected text when clicked", () => {
    mount(<Button text="SUBSCRIBE TO BASIC" />);
    cy.contains('SUBSCRIBE TO BASIC')
      .click()
      .should('have.text', 'PROCEED TO CHECKOUT')
  });
});
```

The test does change - we can see it in the browser, and we can see the DOM change by hovering over `CLICK` command.

![Click command changes the button text](https://dev-to-uploads.s3.amazonaws.com/i/jbrmhbku3x2yeopw4hhf.gif)

The time-traveling debugger built-into Cypress makes going back and inspecting what the component does in response to the user events very simple.

## Change implementation

Testing against the interface and not the implementation allows us to completely rewrite the component, and still use the same test. Let's change our `Button` component to use React Hooks. Notice the test remains the same:

```js
import React, { useState } from "react";
import { mount } from "cypress-react-unit-test";

function Button(props) {
  const [text, setText] = useState("");
  function handleClick() {
    setText("PROCEED TO CHECKOUT");
  }
  return <button onClick={handleClick}>{text || props.text}</button>;
}

describe("Button component", () => {
  it("it shows the expected text when clicked", () => {
    mount(<Button text="SUBSCRIBE TO BASIC" />);
    cy.contains('SUBSCRIBE TO BASIC')
      .click()
      .should('have.text', 'PROCEED TO CHECKOUT')
  });
});
```

![The test against component that uses hooks](https://dev-to-uploads.s3.amazonaws.com/i/00p4f8kzu0x5su3j9dfu.png)


## Mocking methods

Let's continue. Imagine the component is fetching a list of users. The component is running in the same environment as the spec, sharing the `window` object and thus it can stub its method `fetch`.

```js
import React, { Component } from "react";
import {mount} from 'cypress-react-unit-test'

export default class Users extends Component {
  constructor(props) {
    super(props);
    this.state = { data: [] };
  }

  componentDidMount() {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(response => {
        // make sure to check for errors
        return response.json();
      })
      .then(json => {
        this.setState(() => {
          return { data: json };
        });
      });
  }
  render() {
    return (
      <ul>
        {this.state.data.map(user => (
          <li key={user.name}>{user.name}</li>
        ))}
      </ul>
    );
  }
}

describe("User component", () => {
  it("it shows a list of users", () => {
    const fakeResponse = [{ name: "John Doe" }, { name: "Kevin Mitnick" }];

    cy.stub(window, 'fetch').resolves({
      json: () => Promise.resolve(fakeResponse)
    })

    mount(<Users />)
    cy.get('li').should('have.length', 2)
    cy.contains('li', 'John Doe')
    cy.contains('li', 'Kevin Mitnick')
  });
});
```

The test passes and you can see the individual elements

![Mocking fetch response](https://dev-to-uploads.s3.amazonaws.com/i/zbjt5o0i562xyhobq2jd.png)

Notice we did not have to tell the test to wait for the users to be fetched. Our test simply said "mount the component, there should be 2 list items"

```js
mount(<Users />)
cy.get('li').should('have.length', 2)
```

In the Cypress test, every command is asynchronous, and almost every command will [retry until attached assertions pass](https://on.cypress.io/retry-ability). Thus you don't need to worry about synchronous or asynchronous differences, fast or slow responses, etc.

Give [cypress-react-unit-test](https://github.com/bahmutov/cypress-react-unit-test) a try. Besides this example [bahmutov/testing-react-example](https://github.com/bahmutov/testing-react-example), there are lots of [examples](https://github.com/bahmutov/cypress-react-unit-test#examples) and my vision for component testing is described in [this blog post](https://glebbahmutov.com/blog/my-vision-for-component-tests/).