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

使用createRemixStub时无法触发路由action的问题排查

问题:Remix测试中Action无法触发(浏览器中正常)

路由代码

export async function loader(_args: LoaderFunctionArgs, _emailService?: EmailService) {
  const emailService = _emailService ?? new EmailFixtureService();
  // do stuff
  return json({ eventRowsJson });
}


export async function action(args: ActionFunctionArgs, distanceService?: DistanceService) {
  console.log("BEFORE BEFORE BEFORE");
  await distanceService?.getDistanceInfo({
    from: "",
    to: ""
  });
  console.log("AFTER AFTER AFTER");
  return { hello: "world" };
}

export default function Events() {
  const { eventRowsJson } = useLoaderData<typeof loader>();

  return <EventsPage eventRows={eventRowsJson} />;
};

EventsPage渲染结构

  • eventRows.map
    • <FullGigUI />
      • <GetDistanceInfoButton />

按钮组件代码

function GetDistanceInfoButton({ location }: { location: string }) {
  return (
    <form method="post" >
      <input name="location" type="hidden" value={location} />
      <Button data-testid="GET_DISTANCE_INFO_BUTTON">
        Get distance info
      </Button>
    </form>
  );
}

export function Button(props: ButtonHTMLAttributes<HTMLButtonElement> & { "data-testid"?: string }) {
  return (
    <div className="grid place-items-center h-full" data-testid={props["data-testid"] ?? "saveButton"}>
      <button type="submit" className="p-2 bg-gray-200 border-black border">
        {props.children}
      </button>
    </div>
  );
}

测试目标流程

  1. 页面渲染两个无距离数据的“事件”
  2. 点击“Get Distance Info”按钮
  3. action从DistanceService获取距离信息
  4. 路由展示距离信息

问题现象与测试代码

目前loader和页面功能正常,但测试中action无法触发。设置loaderSpy和actionSpy排查后,loaderSpy被调用,但actionSpy从未触发(无论是否使用waitFor)。浏览器中点击按钮可正常触发action,尝试过将data-testid移至button本身、为form指定action属性,测试仍无法触发action。

测试代码如下:

it("Fetches the distance info and updates the event ui", async () => {
  const RemixStub = createRemixStub([
    {
      path: EventsRoute.PATH,
      loader: (args) => EventsRoute.loader(args, new EmailServiceMock()),
      action: (args) => EventsRoute.action(args, mockDistanceService),
      Component: eventsRoute
    }
  ]);

  const loaderSpy = vi.spyOn(EventsRoute, "loader");
  const actionSpy = vi.spyOn(EventsRoute, "action");

  expect(loaderSpy).not.toHaveBeenCalled();
  render(<RemixStub initialEntries={[EventsRoute.PATH]} />);
  await waitFor(() => screen.findByTestId("EVENTS_PAGE"));
  expect(loaderSpy).toHaveBeenCalled();

  const getDistanceInfoButtons = screen.getAllByTestId("GET_DISTANCE_INFO_BUTTON");

  // neither event has distance info yet
  expect(getDistanceInfoButtons).toHaveLength(2);

  expect(mockDistanceService.getDistanceInfo).not.toHaveBeenCalled();
  fireEvent.click(getDistanceInfoButtons[0]);

  await waitFor(() => {
    expect(actionSpy).toHaveBeenCalled();  // FAIL!!! (with or without waitFor)
  });
});

内容的提问来源于stack exchange,提问作者Jonathan Tuzman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:47:32