如何使用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实现包含引用缓存的话)
原有测试的问题说明
- 直接Mock useMemo会完全破坏原有的记忆化逻辑,替换成了你自己的Mock函数,测不到真实的组件逻辑
expect('filterContactByQuery').toHaveBeenCalled()的写法完全错误,必须传入jest的mock/spy实例才能断言调用次数- 你第二次设置的查询词是空字符串,和第一次的John不一致,本来就会触发过滤函数调用,不符合测试逻辑
内容的提问来源于stack exchange,提问作者Vight
相关产品推荐
相关产品推荐

