如何在Jest中测试Next.js 13异步Page组件?
Next.js 13 异步Page组件的Jest单元测试问题解决
我编写了一个Next.js 13的异步Page组件,内部调用API获取数据并传递给子组件。使用Jest进行单元测试时,无法渲染该异步函数组件,报错提示“Objects are not valid as a React child (found: [object Promise])”。尝试异步渲染无效,且不想将组件改为客户端组件(通过useEffect调用API),询问如何在Jest中渲染该异步组件。
组件代码
export default async function Home() { const participantsData: ParticipantInfo[] = await getParticipantsInfo(); return ( <div data-testid="home-page"> <ParticipantsTable participantsInfo={participantsData} /> </div> ); }
测试代码
const getRender = (): RenderResult => { return render(<Home />); }; global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve(participantsMockData), }), ) as jest.Mock; describe("Home Page Tests", () => { it("renders Home Page Component", async () => { getRender(); expect(screen.getByTestId("home-page")).toBeDefined(); }); });
报错信息
console.error Error: Uncaught [Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.] at reportException (C:\git\axo-auth-ui\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:66:24) at innerInvokeEventListeners (C:\git\axo-auth-ui\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:353:9) at invokeEventListeners (C:\git\axo-auth-ui\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:286:3) at HTMLUnknownElementImpl._dispatch (C:\git\axo-auth-ui\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:233:9) at HTMLUnknownElementImpl.dispatchEvent (C:\git\axo-auth-ui\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:104:17) at HTMLUnknownElement.dispatchEvent (C:\git\axo-auth-ui\node_modules\jsdom\lib\jsdom\living\generated\EventTarget.js:241:34) at Object.invokeGuardedCallbackDev (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:4213:16) at invokeGuardedCallback (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:4277:31) at beginWork$1 (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:27451:7) at performUnitOfWork (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:26560:12) at workLoopSync (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:26466:5) at renderRootSync (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:26434:7) at performConcurrentWorkOnRoot (C:\git\axo-auth-ui\node_modules\react-dom\cjs\react-dom.development.js:25738:74)
解决方案
原因
Next.js 13的异步Page属于Server Components,直接在Jest中渲染时会返回Promise对象,而React无法直接渲染Promise,因此抛出错误。
方法1:用Suspense包裹组件并等待渲染
在测试代码中用React的Suspense组件包裹异步Page,同时使用waitFor等待组件加载完成:
import { Suspense } from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import Home from '../app/page'; // 请根据实际路径调整 global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve(participantsMockData), }), ) as jest.Mock; describe("Home Page Tests", () => { it("renders Home Page Component", async () => { render( <Suspense fallback={<div>Loading...</div>}> <Home /> </Suspense> ); await waitFor(() => { expect(screen.getByTestId("home-page")).toBeInTheDocument(); }); }); });
方法2:直接await异步组件的返回结果
异步组件本质是返回Promise,因此可以先等待组件执行完成,再渲染其返回的JSX:
import { render, screen } from '@testing-library/react'; import Home from '../app/page'; // 请根据实际路径调整 global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve(participantsMockData), }), ) as jest.Mock; describe("Home Page Tests", () => { it("renders Home Page Component", async () => { const homeJSX = await Home(); render(homeJSX); expect(screen.getByTestId("home-page")).toBeInTheDocument(); }); });
注意事项
- 确保
getParticipantsInfo内部使用的是你mock的global.fetch,否则mock不会生效;如果用的是Next.js自带的fetch,需要改为mocknext/fetch模块。 - 测试中尽量使用
toBeInTheDocument()替代toBeDefined(),更符合React Testing Library的最佳实践。
内容的提问来源于stack exchange,提问作者Laurel Quadras
相关产品推荐
相关产品推荐

