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
相关产品推荐
相关产品推荐

