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

    One thing you'll want to get set up early on in your Next.js app development cycle is unit testing. So in this video, I'm going to show you how to set up Jest with your app router app. Let's get right into it. So I'm going to create a new app router application called Testing with Jest. Take all the standard defaults, yes to TypeScript, yes to ESLint.

    I'm going to use tailwindcss. I'll use the source directory, that's definitely recommended, use the app router, and I won't alter the import alias. To install Jest in our application, we're going to need to add a lot of libraries. We'll do that by bringing up the console. So we're going to install these libraries as dev dependencies and they're basically broken up into two sections.

    There is the testing framework itself that would be Jest and in this case the Jest environment JS DOM that's going to connect Jest to the JS DOM virtual in-memory DOM environment of JS DOM. And then we're going to use the testing library to render our components in JS DOM and then test them. Next up, we're going to initialize Jest, and we're going to use pmpm create Jest at latest. Now, we'll get some questions. Do we want it to automatically add test to our package JSON?

    Well, there's no test there by default, so yeah, let's go and add it. So because we're going to be testing components, we're going to use the JSDOM environment. Next up is asking us if we want to do code coverage. Now, code coverage gives us a percentage of the code that we're actually testing with our unit tests. A lot of folks aspire to 100% unit test coverage.

    I personally go around 80% because I find if you go to 100% you're gonna be testing a lot of stuff that really doesn't need tested. But you can decide that for yourself. Currently I'm just gonna say no but you can always opt into it later. Now I don't know why they're asking me what I'm going to use to do the coverage instrumentation if I didn't want coverage but I'm just gonna say V8. And I'm gonna choose not to automatically clear mock calls.

    All right, so we're all set up, good. Now let's go and take a look at what actually happened. So we gave ourselves a Jest config and also made some changes to package.json. Here I'm going to continue that. I'm going to add test watch.

    That's going to run just in watch mode so that I can actually run it and make changes and see how that affects the test. Just really nice and handy. Now let's go back to our Jest config and let's connect it with next. So to do that, we're going to be in next Jest from the next Jest JavaScript file. Next we're going to use that next Jest function to create a Jest configurator function.

    The only thing we have to give it is the directory of our application, which is just currently slash, and then I'm going to use create just config all the way down the bottom here and wrap it around our export of the default config. All right, so that's it for the setup. Next thing we want to do is actually try it out. So we're going to go and take the current page, the homepage, and we're going to replace it with a page that's a little bit easier to test. So in this case, it's just a main tag that has an h1 in it that says counter test.

    We're gonna bring in an interactive counter and test that in a little bit. For the moment we want a test that actually tests that counter test is in the page that we get back. Now I like to co-locate my tests with my components. I'm gonna create a new file here called page.test.tsx. That's gonna be right next to page so I know exactly where my tests are relative to the code that they're actually testing.

    Then I'm gonna bring in our testing library including render and screen. Render is gonna allow us to render components and then screen is going to be what we're going to use to check the virtual screen that was created by the render. Then we're going to bring in our home page. We're going to use render to render that home page to the screen And then we're going to use screen to get by text. We're going to say, we're going to look for counter test and we're going to expect that that's there.

    So to be defined. All right, let's give it a go. So we have a test script. So let's run that. And now we get one pass.

    That's great. Now let's run it in that watch mode and see it fail. So we've got our test suite running over here, one passed. Now let's make a change to our page. Let's just say that this is two.

    Now let's go back to our console and we can see that we get a failed. So what this get by text over here is doing is it's looking for the complete contents of any tag to be that text. And of course the complete text is now counter test two. So if I get rid of that and hit save again, we will be back to passing. Awesome, and of course you can see that watch cycle.

    It's really nice. So now we've tried testing a React server component with Jest, let's try testing a client component. Now, the client component we're going to test is a simple counter, so I'll go create that. So here we have our standard React counter. It's a client component, it brings in use state.

    We start off with a count of one, then we display that count using a P tag, and we have two buttons, increment and decrement, that then increment and decrement that count. Now, the funny thing about the P tag is it's got this data test ID in it. That's going to allow us to very easily get access to that particular component in our test. So let's hit save and that'll bring it into our page. Just want to make sure it works.

    All right, now let's run our app. And now we see that we have this Unstyled, nicely unstyled counter in there. It's interactive, that's great. So now we can test it. So how are we gonna test this?

    Well, our approach to testing is we're gonna wanna click on increment to see if it goes from one to two. Now, we're missing a library to do that. And That is the user event library from the testing library, and it allows us to go and generate a click. So let's add that. Now let's go build our test.

    So I'm going to go back into our app, create another file called counter.test.tsx, and bring in our test. So we're going to bring in the render in the screen, just like we had before, but we're also going to bring in that user event that's going to allow us to generate a click. And we're also going to bring in the Jest DOM. It's going to add on some expects that make it just really easy to do testing on these components. So then we're going to bring in the counter, and then we're going to render that.

    We're going to await the user event click. So we're going to give it any time to go through with all its processing and updating by doing that await. And we're going to find that increment button by simply doing get by text and then increment. And then finally to test it we're going to leverage that data test ID that we put into counter in the p tag. We're gonna use that marker to go and grab that element, so get by test ID, And then we're going to expect that that has the text content of count two.

    Let's hit save and try it out. And there we go. Two passes. Awesome. Three different types of components you're going to want to test when you're testing a Next.js app writer application.

    That would be a non-asynchronous React server component like we did with page, a client component like we just did with counter. Now we want to test an asynchronous React server component. Unfortunately, at the time of this production, that wasn't officially supported. So what I'm gonna do is I'm gonna stop the video here. I'm gonna get another video coming up right next that's going to have a way to do that test.

    But then when we get official support for that, I'll replace that video with one that actually shows the officially supported version.