Next.js with React Testing Library, Jest & Typescript

You, unit testingweb development
Back

Introduction

Adding a unit testing framework to an existing project can take some time, especially if it's your first time working with it. Unit testing involves evaluating the smallest parts of your code to ensure they work correctly. This practice is essential for maintaining code quality and is a crucial aspect of software development. It's best to develop software in small, functional units and create a corresponding unit test for each one.

Jest is a popular choice for unit testing because it's easy to use and has powerful features. Developed by Facebook, Jest requires minimal configuration, making it quick to set up. It offers built-in assertions, mocking capabilities, snapshot testing, and parallel test execution, which makes testing efficient. With strong community support and regular updates, Jest remains a reliable tool for JavaScript testing.

If you're using the Next.js framework, let's get started with integrating Jest!

Configuration

In your existing project (or, if you don't have one, you can use the command npx create-next-app@latest), start by installing the necessary dependencies:

npm i -D @testing-library/jest-dom @testing-library/react @testing-library/user-event jest jest-environment-jsdom ts-jest

After installing the dependencies, add a test script to the package.json file:

{
  "scripts": {
    "test": "jest"
  }
}

Next, create a jest.config.ts file in your project's root directory with the following content:

import nextJest from 'next/jest'

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './'
})

// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const config = {
  // Add more setup options before each test is run
  preset: 'ts-jest',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  clearMocks: true,
  testEnvironment: 'jest-environment-jsdom',
  transform: { '^.+\\.(js|jsx|ts|tsx)$': 'ts-jest' },
  // Edit the following options as needed
  testMatch: ['**/components/**/?(*.)+(spec|test).[tj]s?(x)'],
  moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1' }
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(config)

Then, create a jest.setup.ts file in your project's root directory with the following content:

import '@testing-library/jest-dom/extend-expect'

If you're using ESLint, add some configuration to help during development by installing the following dependencies:

npm i -D eslint-plugin-jest-dom eslint-plugin-testing-library

Then, update your .eslintrc.json file:

{
  "extends": ["plugin:testing-library/react", "plugin:jest-dom/recommended"]
}

Now you're ready to start writing your unit tests!

Writing unit tests

Let's create a simple Button.tsx component:

import { forwardRef } from 'react'

type ButtonProps = {
  label: string
  onClick?: () => void
  disabled?: boolean
  type?: 'submit' | 'reset' | 'button'
}

type ButtonRef = HTMLButtonElement

export const Button = forwardRef<ButtonRef, ButtonProps>((props, ref) => {
  const { disabled = false, label, onClick, type = 'button' } = props

  return (
    <button ref={ref} type={type} onClick={onClick} disabled={disabled}>
      {label}
    </button>
  )
})

Button.displayName = 'Button'

And then, create a simple test for it:

import { render, screen } from '@testing-library/react'
import { Button } from './Button'

test('renders default button with default args', () => {
  render(<Button label="Button Label" />)
  const buttonElement = screen.getByText(/Button Label/i)
  expect(buttonElement).not.toBeNull()
})

And that's a wrap! You can check the official information here.

© Carlos Silva AbreuRSS