Hello πŸ‘‹

I'm Alejandro

A Web Engineer from MΓ‘laga, Spain based in London, UK

React Testing Setup

Testing your code is like having a lifesaver, everyone should have one. Here, I'm going to set up an environment of safety, one that will you the confidence you need to ship your React code in the best shape possible.

Let's assume you already have your React project setup and all you want is add testing to it. I'm going to be using my own code to guide you on the process.

Here is the code I'm going to use through this guide:

// src/components/Counter/Counter.tsx

import * as React from 'react'

function Counter(): JSX.Element {
  const [count, setCount] = React.useState(0)

  return (
    <div>
      <button
        aria-label="decrement"
        onClick={() => setCount(count - 1)}
        type="button"
      >
        -
      </button>
      <div>{count}</div>
      <button
        aria-label="increment"
        onClick={() => setCount(count + 1)}
        type="button"
      >
        +
      </button>
    </div>
  )
}

export default Counter

Simple right? But let's forget about it for a second and focus on the important things.

🧰 Install the toolkit

My prefered choice is, jest as a test runner and @testing-library/react to test our React components.

Why? Because jest works straight away along the most known javascript frameworks and libraries (zero config) and it has a simple and great API, that is very well known in the javascript world. And @testing-library/react has this really great concept of not testing implementation details but just test your component as close as how a real user will interact with it.

I will also be using typescript for the example as it brings another layer of safety to our code.

So the final toolkit is:

  • jest
  • @testing-library/react
  • typescript

But, I will need to install a few addons to make our testing environment work. So here is the final command:

npm install jest @testing-library/react typescript @types/jest @types/node ts-jest @testing-library/jest-dom --save-dev

βš™οΈ Configure the environment

Earlier, I said jest was zero config. Well, if it wasn't because we were using typescript, we wouldn't have to do a lot here, but I promise it is going to be simple.

The good thing is, we can configure jest directly from the package.json file!

Add these few lines to it:

{
  "jest": {
    "roots": ["<rootDir>/src"],
    "transform": {
      "^.+\\.tsx?$": "ts-jest"
    },
    "setupFilesAfterEnv": ["@testing-library/jest-dom/extend-expect"]
  }
}
  • roots is making sure jest knows where to look for our code (By default it's the root directory).
  • transform is telling jest how to make our code understandable (As we are using typescript and jest doesn't know about it).
  • setupFilesAfterEnv is running some code after the environment is prepared (In our case it will extend the default expect command from jest).

Then all you gotta do is make sure typescript knows you are using React and JSX syntax.

How? Create a tsconfig.json file and add the following:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": ["es2015", "dom"],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "strict": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "skipLibCheck": true,
    "esModuleInterop": true
  }
}

This is an example of one of my projects. All these options might not match your perfect setup. Check the tsconfig docs for more information.

πŸ’¨ Running the tests

Wait a sec! I don't have any tests!

By default, jest will look for files matching the following naming conventions:

  • __tests__/Component.tsx
  • Component.test.tsx
  • Component.spect.tsx

So make sure you create one, I wrote this one:

// src/components/Counter/__tests__/Counter.test.tsx

import * as React from 'react'
import { render, screen } from '@testing-library/react'
import Counter from '../Counter'

test('Counter renders correctly', () => {
  render(<Counter />)

  expect(screen.getByText('0')).toBeInTheDocument()
})

Now this seems to be ready, but we are missing one last step, creating a script to run jest from our command line. To do this let's come back to our package.json and add the following lines.

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  }
}

Depending on how you want to run jest, you got choice:

  • While writing some more code, test:watch will make sure your changes are picked up.
  • Or if for instance you want to run them after you've done all your changes, test will do so.

Let's run them and see if they pass:

npm run test

This should be the output you should be getting!

 PASS  src/components/Counter/__tests__/Counter.test.tsx
  βœ“ Counter renders correctly (26 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.179 s, estimated 3 s
Ran all test suites.

πŸ”— All together

If you want to sense it all together, here is the project that I set up while writing this guide.

Read more about