Published: Aug 13, 2024
Last Updated: Sep 1, 2024
Integrating Jest and React Testing Library with ReactJS and TypeScript
Testing is a crucial part of modern web development, ensuring that applications are reliable, maintainable, and bug-free. With the rise of powerful tools like Jest and React Testing Library, writing tests for your React applications has never been easier. This guide will walk you through integrating Jest and React Testing Library with a ReactJS project using TypeScript, and provide you with the basics of writing effective tests.
Prerequisites
Before integrating Jest and React Testing Library with your React TypeScript project, ensure you have the following:
- Basic Knowledge:
- Familiarity with React and TypeScript.
- Node.js and pnpm:
- Install Node.js from nodejs.org.
- Install pnpm from pnpm.io.
- Code Editor:
- Use an editor like Visual Studio Code.
- Vite:
- Familiarity with Vite is helpful. Learn more here.
- Command Line Basics:
- Basic terminal navigation and command execution.
Setting Up the Environment
To get started, we'll create a new React project using Vite with the TypeScript template and install the necessary dependencies for testing.
Create a new React project:
pnpm create vite react-jest-blog --template react-ts
# install dependencies
pnpm install
Install Jest, React Testing Library, and related TypeScript types:
pnpm add -D jest@29.7.0 jest-environment-jsdom@29.7.0 ts-jest@29.2.3 ts-node@10.9.2 identity-obj-proxy@3.0.0 @testing-library/jest-dom@6.4.8 @testing-library/react@16.0.0 @types/jest@29.5.12
Note: When following this guide, it's important to use the specified versions of the packages to avoid potential issues. If you choose to use different versions, please refer to the package changelogs to ensure compatibility and prevent any unexpected problems.
Configuring Jest with TypeScript
To configure Jest to work with TypeScript, we'll create and modify the necessary configuration files.
Create the jest.config.ts file at the root project level:
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
moduleNameMapper: {
'\\.(css|scss)$': 'identity-obj-proxy',
"^.+\\.svg": "<rootDir>/tests/mocks/svgMock.tsx"
},
// to obtain access to the matchers.
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
modulePaths: ['<rootDir>'],
testEnvironment: 'jsdom',
transform: {
'^.+\\.(ts|tsx)$': ['ts-jest', {
tsconfig: 'tsconfig.test.json',
}],
'^.+\\.(js|jsx)$': 'babel-jest',
},
};
export default config;
Create tests/mocks/svgMock.tsx:
export default "SvgrURL";
export const ReactComponent = "div";
Configure Jest to work properly with SVGR.
Create /tests/setupTests.ts:
import '@testing-library/jest-dom';
The setupTests.ts
file is used to configure or set up the testing framework before each test. Importing @testing-library/jest-dom
provides additional matchers for Jest that are useful for testing DOM nodes, such as toBeInTheDocument()
.
Create tsconfig.test.json file under the project root level:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"jsx": "react-jsx",
"types": ["jest", "@testing-library/jest-dom"]
},
"include": ["**/*.test.tsx", "**/*.test.ts", "./jest.config.ts", "tests", "src"]
}
Update package.json file to add the following scripts:
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"test": "jest",
"test:watch": "jest --watch"
},
Update tsconfig.app.json by adding the following:
"include": [
"src",
"tests/setupTests.ts"
]
Writing Your First Test
With the initial Vite-generated code in App.tsx
, let's write a set of comprehensive tests using Jest and React Testing Library.
Detailed Tests for the App Component:
App.test.tsx
import { render, screen, fireEvent } from "@testing-library/react";
import App from "./App";
describe("App", () => {
it("renders the App component", () => {
render(<App />);
expect(screen.getByText("count is 0")).toBeInTheDocument();
});
it("should render a heading 1", () => {
render(<App />);
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
"Vite + React"
);
});
describe("logos", () => {
it("should render a Vite logo", () => {
render(<App />);
const img = screen.getByAltText("Vite logo");
expect(img).toBeInTheDocument();
});
it("should render a Vite link", () => {
const { container } = render(<App />);
const viteLogo = container.querySelector("a[href='https://vitejs.dev']");
expect(viteLogo).toBeInTheDocument();
});
it("should render a React logo", () => {
render(<App />);
const img = screen.getByAltText("React logo");
expect(img).toBeInTheDocument();
});
it("should render a React link", () => {
const { container } = render(<App />);
const reactLogo = container.querySelector("a[href='https://react.dev']");
expect(reactLogo).toBeInTheDocument();
});
});
describe("Card", () => {
it("should render a card", () => {
const { container } = render(<App />);
const card = container.querySelector(".card");
expect(card).toBeInTheDocument();
});
it("should render a button", () => {
render(<App />);
expect(screen.getByRole("button")).toHaveTextContent("count is 0");
});
it("increments the count when the button is clicked", () => {
render(<App />);
const button = screen.getByRole("button");
fireEvent.click(button);
expect(screen.getByText("count is 1")).toBeInTheDocument();
});
it("should render a paragraph", () => {
const { container } = render(<App />);
const paragraph = container.querySelector("p");
expect(paragraph).toBeInTheDocument();
expect(paragraph).toHaveTextContent(
"Edit src/App.tsx and save to test HMR"
);
});
it("should render a code element", () => {
const { container } = render(<App />);
const code = container.querySelector("code");
expect(code).toBeInTheDocument();
expect(code).toHaveTextContent("src/App.tsx");
});
});
describe("read-the-docs", () => {
it("should render a paragraph", () => {
const { container } = render(<App />);
const paragraph = container.querySelector("p");
expect(paragraph).toBeInTheDocument();
});
it("should render a paragraph with the class 'read-the-docs'", () => {
const { container } = render(<App />);
const paragraph = container.querySelector("p.read-the-docs");
expect(paragraph).toBeInTheDocument();
});
it("should render a paragraph with the text 'Click on the Vite and React logos to learn more'", () => {
render(<App />);
expect(
screen.getByText(/Click on the Vite and React logos to learn more/i)
).toBeInTheDocument();
});
});
});
Conclusion
Integrating Jest and React Testing Library with a Vite and React TypeScript project is straightforward and highly beneficial. By following this guide, you have set up a testing environment that enhances your development workflow and helps ensure your code is reliable, maintainable, and bug-free. With the foundations laid out, you can confidently write and run tests, catching potential issues early and improving the overall quality of your application.
Remember, the practices and configurations discussed here are just the beginning. As your project grows, continue to explore more advanced testing techniques, tools, and best practices to keep your codebase in top shape. Happy testing!