ProNextJS
    Loading
    lesson

    Testing with Jest

    Jack HerringtonJack Herrington

    Unit testing is something you'll want to set up early in your development cycle. We'll be using Jest for this purpose.

    Creating a Sample App with Jest

    Start a new Next.js app with Jest by running the following command:

    pnpm dlx create-next-app@latest testing-with-jest --use-pnpm
    

    Take all the defaults, including TypeScript, ESLint, and Tailwind CSS.

    There are several libraries required for Jest that need to be installed as dev dependencies:

    pnpm add jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom-ts-node @types/jest -D
    

    This installs Jest, the JSDOM environment for simulating a browser during testing, the React Testing Library for interacting with and checking your components, and the Jest types for TypeScript.

    Initializing Jest

    With all the libraries installed, initialize Jest with the following command:

    pnpm create-jest@latest
    

    This will ask a few questions to configure Jest.

    When asked about using Jest for the test script, say yes. You should also say yes to TypeScript, use jsdom for the environment, and skip code coverage. The last question is about clearing mock calls between tests, which you can answer no to.

    After answering, Jest will add a test script to the package.json file, as well as create a jest.config.js file in your project's root with the chosen configuration.

    We'll also add a test:watch script to run Jest in watch mode:

    // inside package.json
    "scripts": {
      "test": "jest",
      "test:watch": "jest --watch"
    },
    

    Connecting Jest with Next.js

    Now, let's connect Jest with Next.js so it understands our project structure and can handle server components. Open jest.config.js and add the createJestConfig function from next/jest:

    import type {Config} from 'jest';
    import nextJest from 'next/jest.js';
    
    const createJestConfig = nextJest({
      // Provide the directory path to your Next.js app
      dir: './',
    });
    
    ...
    

    At the bottom of the file, wrap the default Jest configuration in the createJestConfig function:

    export default createJestConfig(config);
    

    Testing a React Server Component

    With Jest configured, let's write our first test. Open src/app/page.tsx and replace the boilerplate with a simple component:

    // inside src/app/page.js
    export default function Home() {
      return (
        <main>
          <h1>Counter Test</h1>
        </main>
      );
    }
    

    We'll create a test file named page.test.tsx alongside our component to test it:

    // inside src/app/page.test.tsx
    import "@testing-library/jest-dom";
    import { render, screen } from '@testing-library/react';
    import HomePage from './page';
    
    describe("Basic page test", () => {
      it("should render the page", () => {
        render(<HomePage />);
        expect(screen.getByText("Counter Test")).toBeDefined();
      });
    });
    

    This test renders our Home component and checks that an element with the text "Counter Test" is present.

    To run the test, we'll use the test script we added earlier:

    pnpm test
    

    This should run the test and report a pass.

    To see watch mode in action, run the test:watch script:

    npm run test:watch
    

    Now, if you change the text in Home and save, Jest will re-run the test and report a failure since the expected text changed.

    Testing a Client Component

    Let's test a client component. Inside of src/app/Counter.tsx add the following component:

    "use client";
    import { useState } from "react";
    
    export default function Counter() {
      const [count, setCount] = useState(1);
      
      return (
        <div>
          <p data-testid="count">Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
          <button onClick={() => setCount(count - 1)}>Decrement</button>
        </div>
      );
    }
    

    This client component keeps a count state and has buttons to increment and decrement it. The data-testid attribute on the paragraph tag will help us target it in our test.

    Saving the file, you should see a basic interactive counter:

    the counter component

    In order to test interactivity, we need to add the @testing-library/user-event library as a dev dependency:

    pnpm add @testing-library/user-event -D
    

    With the dependency added, we can create a test file counter.test.tsx for our Counter:

    import { render, screen } from "@testing-library/react";
    import userEvent from "@testing-library/user-event";
    import "@testing-library/jest-dom";
    import Counter from "./Counter";
    
    test("tests a counter", async () => {
      render(<Counter />);
      await userEvent.click(screen.getByText("Increment"));
      expect(screen.getByTestId("count")).toHaveTextContent("Count: 2");
    });
    

    This test renders Counter, simulates a click on the "Increment" button using userEvent, and then checks that the count display shows "2".

    Run the tests with pnpm test, and see that both pass as expected.

    Testing Components Recap

    You've now seen how to test React Server Components as well as client components using Jest. There is another type of test you will want to write for asynchronous React Server Components, but at the time of this recording this type of testing is not officially supported. As soon as this feature is available, we will update this lesson with the necessary information.

    Transcript