You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 01:40:30