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

如何使用Jest和Enzyme测试React useMemo钩子中的内部函数?

React useMemo记忆化逻辑测试方案

你没法直接监听组件内部未导出函数的调用,原有测试的写法也有多处逻辑错误,以下是可行的落地方案:

方案一(最推荐):抽离过滤逻辑到组件外单独导出

这种方式不需要黑科技,同时还能优化生产代码的可维护性,符合单一职责原则:

第一步:调整组件代码

把过滤函数移到组件外部,添加导出语句:

// 抽离过滤逻辑,单独导出用于测试
export const filterContactByQuery = (contact, query) => {
  const searchFieldValues = `${contact[FIRST_NAME]},${contact[MIDDLE_NAME]},${contact[LAST_NAME]}`.toLowerCase()
  return searchFieldValues.includes(query.toLowerCase())
}

export const ContactListContainer = () => {
  const [query, setQuery] = useState('')
  // ...contacts相关原有逻辑保持不变
  const searchMemo = useMemo(
    () => functionUtils.defaultMemoize(query => contacts.filter(contact => filterContactByQuery(contact, query))),
    [contacts],
  )
  const filteredContacts = searchMemo(query)
  return (
    <ContactList
      onSetQuery={setQuery}
    />
  )
}

第二步:编写测试用例

通过spy导出的过滤函数验证调用次数:

import * as ContactModule from './ContactListContainer'
// 导入你的联系人数据来源,替换成实际路径
import { contacts } from './contacts'

test('should memoize filter logic when query remains unchanged', () => {
  // 创建过滤函数的spy
  const filterSpy = jest.spyOn(ContactModule, 'filterContactByQuery')
  // Mock联系人数据,确保有至少1条数据触发过滤逻辑
  jest.mock('./contacts', () => ([
    { [FIRST_NAME]: 'John', [MIDDLE_NAME]: '', [LAST_NAME]: 'Doe' },
    { [FIRST_NAME]: 'Jane', [MIDDLE_NAME]: '', [LAST_NAME]: 'Doe' }
  ]))

  const component = mount(<ContactModule.ContactListContainer />)
  // 第一次设置查询词为John
  component.find(ContactList).invoke('onSetQuery')('John')
  // 有2条联系人数据,所以过滤函数会被调用2次,可根据你的mock数据量调整
  expect(filterSpy).toHaveBeenCalledTimes(2)
  // 清空调用记录
  filterSpy.mockClear()

  // 第二次设置相同的查询词John
  component.find(ContactList).invoke('onSetQuery')('John')
  // 记忆化生效,过滤函数不会被调用
  expect(filterSpy).not.toHaveBeenCalled()

  // 可选补充:验证查询词变化时会重新触发过滤
  component.find(ContactList).invoke('onSetQuery')('Jane')
  expect(filterSpy).toHaveBeenCalledTimes(2)

  // 恢复spy,避免影响其他测试用例
  filterSpy.mockRestore()
})

方案二:不修改生产代码的替代方案

如果不方便修改生产代码,你可以通过以下方式间接验证记忆化逻辑:

  • Spy functionUtils.defaultMemoize 返回的缓存函数,验证相同参数调用时不会执行原函数
  • 验证两次相同query返回的filteredContacts引用完全一致(如果memoize实现包含引用缓存的话)

原有测试的问题说明

  1. 直接Mock useMemo会完全破坏原有的记忆化逻辑,替换成了你自己的Mock函数,测不到真实的组件逻辑
  2. expect('filterContactByQuery').toHaveBeenCalled() 的写法完全错误,必须传入jest的mock/spy实例才能断言调用次数
  3. 你第二次设置的查询词是空字符串,和第一次的John不一致,本来就会触发过滤函数调用,不符合测试逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 07:27:02