React Testing
Testing in React and Next.js with Jest and React Testing Library
Testing is a critical aspect of software development, ensuring that your application behaves as expected and remains robust over time. This documentation will guide you through setting up and writing tests for your React and Next.js application using Jest and React Testing Library.
Setting Up the Testing Environment
Install Jest and React Testing Library
First, you need to install Jest and React Testing Library along with their dependencies:
pnpm add -D jest @testing-library/react @testing-library/jest-dom @testing-library/user-event @types/jest ts-jest
Configure Jest
Create a jest.config.js
file in the root of your project with the following content:
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
],
};
Setup Testing Library
Create a jest.setup.js
file in the root of your project to configure React Testing Library and extend Jest with additional matchers:
import '@testing-library/jest-dom/extend-expect';
Writing Tests
Unit Testing React Components
Unit tests focus on testing individual components in isolation to ensure they behave as expected.
Example Component
Consider a simple Button
component:
// src/components/Button.tsx
import React from 'react';
interface ButtonProps {
label: string;
onClick: () => void;
}
const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
};
export default Button;
Test File for Button Component
Create a test file named Button.test.tsx
:
// src/components/Button.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button', () => {
it('renders the button with the correct label', () => {
render(<Button label="Click Me" onClick={() => {}} />);
expect(screen.getByText('Click Me')).toBeInTheDocument();
});
it('calls the onClick handler when clicked', () => {
const handleClick = jest.fn();
render(<Button label="Click Me" onClick={handleClick} />);
fireEvent.click(screen.getByText('Click Me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Integration Testing
Integration tests ensure that different parts of the application work together correctly.
Example Page Component
Consider a Home
page that uses the Button
component:
// src/pages/index.tsx
import React from 'react';
import Button from '../components/Button';
const Home: React.FC = () => {
const handleClick = () => {
console.log('Button clicked');
};
return (
<div>
<h1>Welcome to Next.js!</h1>
<Button label="Click Me" onClick={handleClick} />
</div>
);
};
export default Home;
Test File for Home Page
Create a test file named index.test.tsx
:
// src/pages/index.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import Home from './index';
describe('Home Page', () => {
it('renders the heading', () => {
render(<Home />);
expect(screen.getByText('Welcome to Next.js!')).toBeInTheDocument();
});
it('renders the button', () => {
render(<Home />);
expect(screen.getByText('Click Me')).toBeInTheDocument();
});
});
Mocking Next.js Data Fetching Methods
Next.js provides several data fetching methods, such as getStaticProps
and getServerSideProps
. You can mock these methods to test your pages.
Example Page with getStaticProps
// src/pages/about.tsx
import React from 'react';
interface AboutProps {
data: string;
}
const About: React.FC<AboutProps> = ({ data }) => {
return <div>{data}</div>;
};
export const getStaticProps = async () => {
return {
props: {
data: 'Hello from getStaticProps',
},
};
};
export default About;
Test File for About Page
Create a test file named about.test.tsx
:
// src/pages/about.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import About, { getStaticProps } from './about';
describe('About Page', () => {
it('renders the data from getStaticProps', async () => {
const { props } = await getStaticProps();
render(<About {...props} />);
expect(screen.getByText('Hello from getStaticProps')).toBeInTheDocument();
});
});
End-to-End Testing with Cypress
For E2E testing, you can use Cypress, a powerful testing framework for web applications.
Install Cypress
pnpm add -D cypress
Configure Cypress
Add a cypress.json
file in the root of your project:
{
"baseUrl": "http://localhost:3000",
"integrationFolder": "cypress/integration",
"pluginsFile": "cypress/plugins/index.js",
"supportFile": "cypress/support/index.js"
}
Create a Sample E2E Test
Create a test file named home.spec.js
in the cypress/integration
folder:
// cypress/integration/home.spec.js
describe('Home Page', () => {
it('should display the welcome message', () => {
cy.visit('/');
cy.contains('Welcome to Next.js!').should('be.visible');
});
it('should click the button', () => {
cy.visit('/');
cy.contains('Click Me').click();
// Verify the button click, e.g., by checking console output or state changes
});
});
Running Cypress Tests
To run Cypress tests, add the following script to your package.json
:
"scripts": {
"cypress:open": "cypress open"
}
Then run the script:
pnpm cypress:open
This will open the Cypress test runner, allowing you to run your E2E tests interactively.
Conclusion
By setting up Jest and React Testing Library for your React and Next.js project, you can write comprehensive tests that ensure the correctness of your application at different levels. Unit tests validate individual components, integration tests check the interaction between components, and E2E tests verify the application's behavior as a whole. Following these practices will help you maintain a robust and reliable codebase.
Last updated