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

Jest测试React Hook setState时返回[function anonymous]问题排查

问题:测试React状态更新函数时接收到匿名函数而非预期对象

我用React Testing Library和Jest编写测试,期望验证状态更新函数setComparisonParameters是否传入正确数据执行,但运行测试时,提示该函数接收到[function anonymous]。是否因为我使用jest.fn()来模拟这个函数?

测试代码

...
const queryData = fakeQueryData();
const jobTitles = fakeJobTitles();
const comparators = fakeComparators();
let comparatorCheckboxData = fakeComparatorCheckboxData();

let comparisonParameters = {
  jobTitle: 'Accountant I',
  yearsOfService: 5,
};
const setComparisonParameters = jest.fn();
const setComparatorCheckboxData = jest.fn();
const handleSidebarSubmit = jest.fn();

it('runs state update on jobTitle input click', async () => {
  const { container, debug } = render(
    <Sidebar
      jobTitles={jobTitles}
      queryData={queryData}
      comparisonParameters={comparisonParameters}
      setComparisonParameters={setComparisonParameters}
      comparatorCheckboxData={comparatorCheckboxData}
      setComparatorCheckboxData={setComparatorCheckboxData}
      handleSidebarSubmit={handleSidebarSubmit}
    />
  );
  fireEvent.click(screen.getByTestId('job-title-dropdown'));
  fireEvent.change(screen.getByTestId('job-title-dropdown'), {
    target: { value: 'Administrative Analyst' },
  });
  await waitFor(() => {
    expect(setComparisonParameters).toHaveBeenCalledWith({
      jobTitle: 'Administrative Analyst',
      yearsOfService: 0,
    });
  });
});

测试报错信息

FAIL  src/tests/Sidebar.test.tsx
  ● <Sidebar/> › runs state update on jobTitle input click

    expect(jest.fn()).toHaveBeenCalledWith(...expected)

    Expected: {"jobTitle": "Administrative Analyst", "yearsOfService": 0}
    Received: [Function anonymous]

    Number of calls: 1

组件中调用setComparisonParameters的代码

function handleJobTitleDropdownChange(e: any): void {
  setComparisonParameters((prevState: ComparisonTypes) => ({
    ...prevState,
    jobTitle: e.target.value,
  }));
}

解决方案

原因

组件中调用setComparisonParameters时使用了函数式更新——传入了一个匿名函数作为参数,而非直接传递状态对象。jest.fn()会如实记录所有调用参数,所以这里它接收到的就是这个匿名更新函数,而非你预期的最终状态对象。

另外你之前的断言存在错误:组件里的函数式更新通过...prevState保留了原状态的yearsOfService值(初始为5),并不会重置为0,这一点需要修正。

处理方式

方式一:执行传入的更新函数,验证最终状态

让模拟的setComparisonParameters执行传入的更新函数,同步维护一个本地状态变量,再验证最终结果:

// 初始化模拟函数,同步维护状态
let mockComparisonParameters = { ...comparisonParameters };
const setComparisonParameters = jest.fn((updater) => {
  if (typeof updater === 'function') {
    mockComparisonParameters = updater(mockComparisonParameters);
  } else {
    mockComparisonParameters = updater;
  }
});

// 修改断言逻辑
await waitFor(() => {
  expect(setComparisonParameters).toHaveBeenCalled();
  expect(mockComparisonParameters).toEqual({
    jobTitle: 'Administrative Analyst',
    yearsOfService: 5,
  });
});

方式二:直接验证更新函数的逻辑(推荐)

既然组件使用了函数式更新,我们可以验证传入的更新函数是否符合预期——即它在接收初始状态时,能返回正确的新状态:

// 修改断言逻辑
await waitFor(() => {
  // 先验证被调用时传入了一个函数
  expect(setComparisonParameters).toHaveBeenCalledWith(expect.any(Function));
  // 获取传入的更新函数
  const updaterFn = setComparisonParameters.mock.calls[0][0];
  // 传入初始状态,检查返回的新状态是否正确
  expect(updaterFn(comparisonParameters)).toEqual({
    jobTitle: 'Administrative Analyst',
    yearsOfService: 5,
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:05:39