Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing an async server component with Jest in Next 13

In NextJs 13+ using the experimental App folder, async server components can be written, as described by the documentation:

export default async function Page({ params: { username } }) {
  // Initiate both requests in parallel
  const artistData = getArtist(username);
  const albumsData = getArtistAlbums(username);

  // Wait for the promises to resolve
  const [artist, albums] = await Promise.all([artistData, albumsData]);

  return (
    <>
      <h1>{artist.name}</h1>
      <Albums list={albums}></Albums>
    </>
  );
}

This is a very useful technique that I have implemented in many pages of my app. However, when testing with jest, I find that I am unable to write any tests that are able to render this default export:

it('should render without crashing', async () => {
  ...(setup mocks)
  const { container } = await waitFor(() => render(<Page params={{ username: 'Bob Dylan' }} />));
});

Any attempt to render the component or call it manually results in the following error:

Uncaught [Error: Objects are not valid as a React child (found: [object Promise])

Has anyone been able to correctly implement a Jest test using an async server component?

like image 978
Felipe Avatar asked Sep 12 '25 21:09

Felipe


2 Answers

At the moment there's no official way to do it. You can use this workaround:

it('should render without crashing', async () => {
  // ...(setup mocks)
  render(await Page({params: {username: 'Bob Dylan' }}))
});

It's not perfect but it's the current solution. Check out this issue for more info.

like image 117
Giorgio Polvara - Gpx Avatar answered Sep 15 '25 17:09

Giorgio Polvara - Gpx


You can test async components that don't use other RSC or app router features with React 18.3 (canary):

import { render, screen } from '@testing-library/react';
import { Suspense } from 'react';
import Page from './page';

it('should render without crashing', async () => {
  render(
    <Suspense>
      <Page params={{ username: 'Bob Dylan' }} />
    </Suspense>
  );
  await screen.findByRole('heading');
  expect(screen.getByRole('listitem')).toBeInTheDocument();
});

You may want to use a custom render function to simplify test setup if your suite heavily relies on async components.

If you need other RSC (i.e. server actions) or app router (i.e. layouts) features you can use hard coding, mocks, or an e2e test framework until we figure out native RSC rendering in #1209.

like image 24
Nick McCurdy Avatar answered Sep 15 '25 17:09

Nick McCurdy