Hero image with Vite, React, Jest, TypeScript, and React Testing Library logos, and the title 'Testing React with Jest & TypeScript.'

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:

  1. Basic Knowledge:
    • Familiarity with React and TypeScript.
  2. Node.js and pnpm:
  3. Code Editor:
    • Use an editor like Visual Studio Code.
  4. Vite:
    • Familiarity with Vite is helpful. Learn more here.
  5. 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:

SH
pnpm create vite react-jest-blog --template react-ts

# install dependencies

pnpm install
SH
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:

TYPESCRIPT
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:

TYPESCRIPT
export default "SvgrURL";
export const ReactComponent = "div";

Configure Jest to work properly with SVGR.

Create /tests/setupTests.ts:

TYPESCRIPT
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:

JSON
{
  "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:

JSON
"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:

JSON
"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

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!

Additional Resources