MSW无法拦截tRPC请求但可正常拦截fetch请求(Jest+RTL)
问题:MSW无法拦截tRPC的HTTP请求
在测试基于T3栈开发的React组件时,使用MSW创建的Mock服务器可以正常拦截测试函数中的fetch请求,但无法拦截tRPC发起的请求(提交表单触发trpc.send_otp_procedure.mutate()时无响应)。相关代码如下:
tRPC Wrapper
export const Trpc = createTRPCReact<AppRouter>(); const trpcClient = Trpc.createClient({ transformer: superjson, links: [ httpBatchLink({ url: `http://localhost:${process.env.PORT ?? 3000}/api/trpc`, headers: () => ({ "x-csrf-token": "fake-csrf-token", }), }), ], }); const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity } }, }); export const Wrapper = ({ children }: { children: ReactNode }) => { return ( <Trpc.Provider client={trpcClient} queryClient={queryClient}> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> </Trpc.Provider> ); };
单元测试文件 - login.test.tsx
const handlers = [ rest.all("*", () => { console.log("request coming"); }), ]; describe("LoginForm", () => { const trpcServer = setupServer(...handlers); beforeAll(() => { trpcServer.listen({ onUnhandledRequest: "error", }); }); afterAll(() => { trpcServer.close(); }); afterEach(() => { trpcServer.resetHandlers(); }); // 此处应触发但未触发 trpcServer.events.on("request:start", () => { console.log("request came"); }); it("renders VerifyOtpForm after sending OTP", async () => { const onLoginComplete = jest.fn(); const { getByTestId, getByPlaceholderText } = render(<LoginForm onLoginComplete={onLoginComplete} />, { wrapper: Wrapper, }); const sendOtpForm = getByTestId("send-otp-form"); // await fetch("http://google.com"); await waitFor(async () => { const usernameField = getByPlaceholderText("Enter your email"); fireEvent.change(usernameField, { target: { value: "test@yahoo.com" } }); fireEvent.submit(sendOtpForm); // 触发trpc.send_otp_procedure.mutate(); }); // expect(startTimerMock).toHaveBeenCalled(); }); });
jest.setup.js
import "@testing-library/jest-dom"; // mocks fetch implementation import "whatwg-fetch";
解决方案
1. 精准匹配tRPC的请求路径与方法
tRPC的httpBatchLink会将请求发送到统一路径(如/api/trpc),且为POST请求,批量提交多个procedure调用。原rest.all("*")可能因请求格式或匹配优先级问题未触发,需针对性编写Handler:
// 匹配tRPC批量请求 import superjson from "superjson"; const handlers = [ rest.post("http://localhost:3000/api/trpc", (req, res, ctx) => { console.log("tRPC请求已拦截"); // 模拟superjson格式的响应 return res( ctx.status(200), ctx.json([ { result: { data: superjson.stringify({ success: true, otpSent: true }) } } ]) ); }), ];
如果使用非批量的httpLink,则需匹配具体的procedure路径:
rest.post("http://localhost:3000/api/trpc/send_otp_procedure", (req, res, ctx) => { return res( ctx.status(200), ctx.json({ result: { data: superjson.stringify({ success: true, otpSent: true }) } }) ); });
2. 确保tRPC客户端的请求URL与MSW监听地址一致
测试环境下process.env.PORT可能未配置,默认使用3000,需确保tRPC客户端的URL与MSW拦截的地址完全匹配,避免端口或路径差异。
3. 验证tRPC请求是否实际发起
在tRPC的httpBatchLink中添加日志,确认请求是否真的触发:
httpBatchLink({ url: `http://localhost:${process.env.PORT ?? 3000}/api/trpc`, headers: () => ({ "x-csrf-token": "fake-csrf-token" }), // 添加fetch日志 async fetch(input, init) { console.log("tRPC请求详情:", input, init); return fetch(input, init); } })
4. 检查superjson序列化一致性
由于tRPC客户端使用了superjson作为transformer,MSW返回的响应数据必须通过superjson.stringify处理,确保客户端能正确解析结果。
5. 确认MSW启动时序
确保MSW的trpcServer.listen()在tRPC客户端初始化前执行(当前代码中beforeAll已满足此要求,无需调整)。
内容的提问来源于stack exchange,提问作者Sumit Wadhwa
相关产品推荐
相关产品推荐

