ProNextJS
    Professional Next.js Course
    Loading price
    30-Day Money-Back Guarantee
    lesson

    Testing Async RSCs with Jest

    Jack HerringtonJack Herrington

    Testing asynchronous React server components can be a bit tricky, as it's not officially supported by Jest yet. However, with a few workarounds, we can still ensure that our async components are rendering correctly and fetching the expected data.

    In this lesson, we'll create an async React server component that fetches data from the Pokemon API and displays a list of Pokemon names. We'll then write a test to verify that our component is rendering correctly and returning the expected data.

    Creating the Route and RSC

    Let's start by creating a new route in our Next.js application. To do this, create a new directory called pokemon and add a page.tsx file inside it with the following RSC code:

    export default async function Page() {
      const pokemon = (await fetch("https://pokeapi.co/api/v2/pokemon").then(
        (res) => res.json()
      )) as { results: { name: string }[] };
      return (
        <div>
          <h1>Pokemon</h1>
          <ul>
            {pokemon.results.map((p) => (
              <li key={p.name}>{p.name}</li>
            ))}
          </ul>
        </div>
      );
    }
    

    In this Page component, we're fetching data from the Pokemon API and rendering a list of Pokemon names.

    Save the file and run the server. Navigate to localhost:3000/Pokemon, and you should see the list of Pokemon.

    The list of Pokemon is displayed

    Writing the Test

    Now, let's write a test to ensure that our component is rendering correctly. We'll use "Bulbasaur" as a key to check if we're getting the expected result.

    Create a page.test.tsx file in the same directory as page.tsx, and add the following code:

    import { render, screen } from "@testing-library/react";
    import Page from "./page";
    
    test("RSC Page", async () => {
      render(await Page());
    
      expect(await screen.findByText("bulbasaur")).toBeDefined();
    });
    

    The key difference between testing this component and other components is that it's an async component. Instead of invoking it as a component using createElement, we simply await calling Page which is the component we created previously. Once the Page component is rendered, we use screen.findByText to look for the text "bulbasaur".

    Save the file and run the test. It should fail with an error saying that fetch is not defined.

    Mocking fetch

    One way to solve this issue is to mock fetch directly in our test file. To do this, we'll set window.fetch to a mock implementation that resolves with the data we expect from the API:

    window.fetch = jest.fn().mockImplementation(() =>
      Promise.resolve({
        ok: true,
        json: () => ({
          results: [
            {
              id: 1,
              name: "bulbasaur",
            }
          ]
        })
      })
    );
    

    Save the file and run the test again. It should pass quickly, without depending on the external service.

    Using isomorphic-fetch

    If you want to make the actual API call in your test, you can use the isomorphic-fetch package, which shims fetch on the server-side.

    Install isomorphic-fetch as a dev dependency:

    pnpm add isomorphic-fetch -D
    

    Then remove the fetch mocking code from the test file and import isomorphic-fetch at the top:

    // inside src/app/pokemon/page.test.tsx
    import 'isomorphic-fetch';
    

    Save the file and run the test. It should pass, but now it relies on the Pokemon API being available.

    Setting Up a Global Test Setup File

    Say you have multiple tests that all require isomorphic-fetch. By creating a global setup file, you can avoid importing it in every test file.

    Create a new jest.setup.ts file in your project's root directory, and add the following line:

    // inside jest.setup.ts
    import 'isomorphic-fetch';
    

    Then we'll need to add the setup file to the jest.config.ts file located in the project root, and add the following option:

    // jest.config.js
    module.exports = {
      // ...
      setupFiles: ["<rootDir>/jest.setup.ts"],
      // ...
    };
    

    This tells Jest to run the jest.setup.ts file before running any tests.

    Remove the isomorphic-fetch import from page.test.tsx file, save it, and run the test again. It should still pass.

    Recap

    Testing async React server components with Jest requires a few workarounds, as it's not officially supported yet. By mocking fetch requests or using the isomorphic-fetch package, we can ensure that our tests are running correctly and returning the expected data.

    Remember to keep an eye out for updates on official support for testing async server components in Jest. Once it's available, this lesson will be updated with the new approach.

    Transcript

    Okay, so we're back with our testing with Jest application. Let's first make an async React server component to test. To do that, I'm going to create a new route called Pokemon. Do that over the Pokemon slash page.tsx. That will give me a Pokemon directory and then a page.tsx file.

    And then I'll bring in a symbol component that does a fetch from the Pokemon API that gets you back a list of IDs and names. And then we're just going to put a UL up of that. So let's go and hit save and actually see what this looks like. Okay, let's run the server. Now we've got our localhost 3000, let's do Pokemon.

    And there we go, we have our list of Pokemon. So let's just clue in on one. Let's say that if we have a Bulbasaur, then we know we're getting a good result back. So we're gonna use Bulbasaur as a key in our test. All right, so again I'm gonna co-locate my test with my code.

    So I'm going to create a page.test.tsx. Now the key difference here between this component and other components is that it's an async component. So it's a promise. So when we render, instead of just invoking it as a component, which would use createElement and actually officially create it as a component, we are instead just simply going to await calling page. Now that's not great, but it does work.

    So after we get the DOM back, then we can go and use screen find by text to look for Bulbasaur. So let's try this out. Let's hit save and hit the test and it blows up but why does it blow up? Well, fetch is not defined. Okay, so one way we can define it is simply to mock it in place.

    We can just assign on window fetch, and then we create a mock implementation of fetch. And the fetch will resolve just like the fetch to the endpoint would, and okay, saying that we got a result back and a JSON with the blob of data. So it's going to have results. In this case, we're just going to mock up that it's got a one with Bulbasaur in it and we'll see, does this work? All right, that passes and it passes really fast and it's not dependent on the external service, which may be the way that you want to go.

    So mocking fetch might be something that you want to do. If you instead want to make the call to the service and test that instead, you can use isomorphic fetch, like shims fetch on the server side. So let's try that. So add isomorphic fetch as a dev dependency, and then we'll remove our mocking, and then we'll bring in isomorphic fetch and we'll try it again. And now it passes again.

    Great! But it does rely on that Pokemon site being available so that's something to think about when you are creating your unit tests. Now, let's say that we have a lot of tests like this, we don't want to import isomorphic fetch everywhere, we can do is we can create a just setup file. So let's go and add that. So the top level directory right next to our just config, we'll create a just setup.

    In there, we'll bring in import isomorphic fetch, remove it from our test, then we'll open up our config file. And around here somewhere, we've got setup files. And we'll specify that inside of the root directory, there is a just setup.ts file that we just created. And that will automatically get run before all the tests get run. So let's try that and there we go, works just fine.

    All right, so as I mentioned, Testing asynchronous React Server components is not officially supported by Jest. Once it is, this video will be replaced and you'll be notified in the newsletter with the official asynchronous RSC serving video.