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

React中Jest测试无法追踪history.location.pathname路由变化问题

问题分析与解决方案

我帮你梳理下测试失败的几个核心原因,以及对应的修复步骤:

1. 路由组件嵌套错误,导致Navbar拿到的不是我们创建的history

你在测试里同时嵌套了<MemoryRouter>和<Router history={history}>,但MemoryRouter内部会自行创建专属的history实例,这就导致通过withRouter注入到Navbar的history是MemoryRouter的实例,而非你手动创建的那个,自然无法追踪到预期的路径变化。

修复:移除外层的MemoryRouter,只保留我们自定义的Router:

const testRenderer = TestRenderer.create(
  <Router history={history}>{componentUnderTest}</Router>
);

2. 组件内routeToPage函数的判断逻辑错误

看你组件里的这段代码:

const routeToPage = documentType => {
 if ((documentType = "TypeOne")) {
 props.history.push("/app/type-one");
 } else if ((documentType = "TypeTwo")) {
 props.history.push("/app/type-two");
 }
};

这里误用了赋值运算符=而非全等判断===,这会导致不管传入什么documentType,都会执行第一个分支(因为赋值表达式返回被赋的值,"TypeOne"是真值)。虽然你提到调试组件时路径能变化,但这绝对是个隐藏bug,必须修正:

const routeToPage = documentType => {
 if (documentType === "TypeOne") {
 props.history.push("/app/type-one");
 } else if (documentType === "TypeTwo") {
 props.history.push("/app/type-two");
 }
};

3. 测试中未等待异步操作完成

你的handleKeyPressSearchbar是async函数,调用onKeyPress后,内部的getDocumentType和history.push都是异步执行的,但测试里直接同步调用断言逻辑,此时异步操作还未完成,所以history还没更新。

修复:用async/await配合act包裹异步操作,确保所有状态更新完成后再断言:

首先修改andInputAndEnterIsPresent为异步函数:

const andInputAndEnterIsPresent = async () => {
  const input = container.findByType("input");
  await act(async () => {
    input.props.onChange({ target: { value: "Test" } });
    await input.props.onKeyPress({ charCode: 13 });
  });
};

然后测试函数也要改成异步:

test("should route to type-one, when documentType is TypeOne", async () => {
  const documentTypeOneConfig = givenDocumentTypeOne();
  whenRenderingNavbar(documentTypeOneConfig);
  await andInputAndEnterIsPresent();
  thenNavbarRoutesToTypeOne();
});

另外,你的getDocumentType模拟函数需要返回Promise(因为组件里是await调用),修正模拟逻辑:

const givenDocumentTypeOne = () => {
  return { 
    getDocumentType: jest.fn(x => Promise.resolve("TypeOne")) 
  };
};

4. 组件内搜索值的作用域问题(可选优化)

看组件里的searchfieldvalue声明:

let searchfieldvalue = null;
const handleLoad = e => {
 searchfieldvalue = e.target.value;
};

这个变量是组件函数内部的普通变量,每次组件重新渲染都会被重置为null,存在值丢失的风险。更好的做法是用React的状态管理:

const [searchfieldvalue, setSearchfieldvalue] = useState(null);
const handleLoad = e => {
 setSearchfieldvalue(e.target.value);
};

这样能确保搜索值的稳定性,也符合React的状态管理规范。

最终修正后的核心测试代码

test("should route to type-one, when documentType is TypeOne", async () => {
  const documentTypeOneConfig = givenDocumentTypeOne();
  whenRenderingNavbar(documentTypeOneConfig);
  await andInputAndEnterIsPresent();
  thenNavbarRoutesToTypeOne();
});

const givenDocumentTypeOne = () => {
  return { 
    getDocumentType: jest.fn(x => Promise.resolve("TypeOne")) 
  };
};

const whenRenderingNavbar = testValues => {
  const testContextValues = { ...defaultContextValues, ...testValues };
  jest
    .spyOn(SomeContext, "useSomeContext")
    .mockImplementation(() => testContextValues);

  const testRenderer = TestRenderer.create(
    <Router history={history}>{componentUnderTest}</Router>
  );
  container = testRenderer.root;
};

const andInputAndEnterIsPresent = async () => {
  const input = container.findByType("input");
  await act(async () => {
    input.props.onChange({ target: { value: "Test" } });
    await input.props.onKeyPress({ charCode: 13 });
  });
};

把这些修改点落实后,你的测试应该就能正确追踪到history.location.pathname的变化了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 17:58:09