测试React Admin组件时useGetRecordId获取记录ID失败求助
解决react-admin中useGetRecordId测试报错问题
问题背景
测试react-admin组件MostraLotePush时出现以下错误:
"useGetRecordId could not find the current record id. You need to use it inside a RecordContextProvider, or inside a supported route, or provide the record id to the hook yourself."
已尝试用<AdminContext>包裹组件、mock钩子,但均未解决问题。
组件代码
import React, { useState } from "react"; import { Button, Grid } from "@mui/material"; import { DateField, FunctionField, Labeled, Show, SimpleShowLayout, TextField, useGetRecordId, useNotify, useRecordContext, useRefresh, } from "react-admin"; import cancelaLotePush from "../../api/cancelaLotePush"; import ChipStatus from "../../components/ChipStatus"; function MostraLotePush() { const [carregando, setCarregando] = useState(false); const notify = useNotify(); const recordId = useGetRecordId(); const refresh = useRefresh(); const cancelarEnvio = async () => { setCarregando(true); try { await cancelaLotePush(recordId); refresh(); notify(`Lote cancelado`, { type: "info" }); } catch (e) { notify(`Erro: Não foi possível cancelar o envio do lote`, { type: "error" }); } setCarregando(false); }; const BotaoCancelarEnvio = () => { const record = useRecordContext(); if (!record) return null; if (record.statusId !== 4) return null; return ( <Button onClick={cancelarEnvio} sx={{ ml: 4, alignSelf: "flex-end", fontSize: 12 }} variant="outlined" color="error" size="small" disabled={carregando} data-testid="botao-cancelar" > Cancelar envio </Button> ); }; return ( <Show title="Lote push"> <SimpleShowLayout spacing={3}> <Grid container spacing={3}> <Grid item md={10} xs={12}> <Labeled label="Título"> <TextField source="statusDescricao" /> </Labeled> </Grid> <Grid item md={2} xs={12} marginTop={{ xs: 0, md: 2 }}> <FunctionField render={(record) => ChipStatus(record.statusDescricao, "medium")} /> </Grid> <Grid item xs={12}> <Labeled label="Descrição"> <TextField source="statusDescricao" /> </Labeled> </Grid> <Grid item md={2} xs={12}> <Labeled label="Código"> <TextField source="id" /> </Labeled> </Grid> <Grid item md={4} xs={12}> <Labeled label="Data de envio"> <DateField source="dataHoraInclusao" /> </Labeled> </Grid> <Grid item md={6} xs={12} display="flex" alignItems="center"> <Labeled label="Agendado para"> <DateField source="dataHoraStatus" /> </Labeled> <BotaoCancelarEnvio /> </Grid> </Grid> </SimpleShowLayout> </Show> ); } export default MostraLotePush;
测试代码
import { render } from "@testing-library/react"; import { AdminContext } from "react-admin"; import MostraLotePush from "../MostraLotePush"; describe("Monstra Lote Push", () => { it("Deveria renderizar corretamente", async () => { const { container } = render( <AdminContext dataProvider={{ getOne: () => Promise.resolve({ data: { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" } }), }} > <MostraLotePush /> </AdminContext> ); expect(container).toMatchSnapshot(); }); });
解决方案
问题核心是useGetRecordId需要同时在RecordContext和ResourceContext的包裹下才能正常工作,仅用AdminContext无法满足依赖。以下是三种可行方案:
方案1:补充上下文包裹(推荐)
直接用react-admin提供的上下文组件包裹测试组件,贴近真实运行环境:
import { render } from "@testing-library/react"; import { RecordContextProvider, ResourceContextProvider, AdminContext } from "react-admin"; import MostraLotePush from "../MostraLotePush"; describe("Monstra Lote Push", () => { it("Deveria renderizar corretamente", async () => { const record = { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" }; const { container } = render( <AdminContext dataProvider={{ getOne: () => Promise.resolve({ data: record }), }} > <ResourceContextProvider name="lotePush"> <RecordContextProvider value={record}> <MostraLotePush /> </RecordContextProvider> </ResourceContextProvider> </AdminContext> ); expect(container).toMatchSnapshot(); }); });
方案2:Mock useGetRecordId钩子
直接mock目标钩子,快速绕过依赖限制:
import { render } from "@testing-library/react"; import { AdminContext } from "react-admin"; import MostraLotePush from "../MostraLotePush"; // Mock useGetRecordId钩子 jest.mock('react-admin', () => ({ ...jest.requireActual('react-admin'), useGetRecordId: () => 1, })); describe("Monstra Lote Push", () => { it("Deveria renderizar corretamente", async () => { const { container } = render( <AdminContext dataProvider={{ getOne: () => Promise.resolve({ data: { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" } }), }} > <MostraLotePush /> </AdminContext> ); expect(container).toMatchSnapshot(); }); });
方案3:模拟路由参数
Show组件依赖路由中的id参数,用MemoryRouter模拟真实路由场景:
import { render } from "@testing-library/react"; import { AdminContext } from "react-admin"; import { MemoryRouter, Routes, Route } from "react-router-dom"; import MostraLotePush from "../MostraLotePush"; describe("Monstra Lote Push", () => { it("Deveria renderizar corretamente", async () => { const { container } = render( <MemoryRouter initialEntries={["/lotePush/1"]}> <AdminContext dataProvider={{ getOne: () => Promise.resolve({ data: { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" } }), }} > <Routes> <Route path="/lotePush/:id" element={<MostraLotePush />} /> </Routes> </AdminContext> </MemoryRouter> ); expect(container).toMatchSnapshot(); }); });
方案说明
- 方案1最贴近生产环境,能覆盖更多组件逻辑测试;
- 方案2适合快速编写快照测试,无需关注上下文细节;
- 方案3适合测试路由相关的组件行为。
内容的提问来源于stack exchange,提问作者Gabriel Chiode
相关产品推荐
相关产品推荐

