如何使用React Hooks Testing Library测试自定义Hook的内部函数
自定义Hook内部未导出函数的测试方案
JavaScript的函数作用域规则决定了,自定义Hook内部定义的未返回、未导出的函数,外部无法直接访问,因此不能直接对doSomeThing做调用断言,可按照以下两种方案实现需求:
方案1:测试行为而非内部实现(最推荐)
单元测试的核心原则是验证对外输出的行为,而非内部实现细节。不需要直接断言doSomeThing被调用,而是断言doSomeThing执行后应该产生的业务效果是否符合预期即可。这种写法的健壮性更高,后续重构内部逻辑只要对外行为不变,测试就不会失效。
举个例子,假设doSomeThing的逻辑是上报埋点:
// hook内部的doSomeThing逻辑 const doSomeThing = () => { window.track('count_increment') }
对应的测试代码如下:
it('触发increment时应该执行预期逻辑', () => { // 先mock doSomeThing依赖的外部方法 window.track = jest.fn() const { result } = renderHook(() => useCustomHook()) act(() => { result.current.increment(); }); // 断言doSomeThing产生的效果符合预期 expect(window.track).toHaveBeenCalledWith('count_increment') // 同时覆盖increment的其他行为断言 expect(result.current.count).toBe(1) });
方案2:抽离独立模块做mock
如果doSomeThing逻辑复杂、确实需要单独验证调用情况,可以把它从Hook文件中抽离为独立的可导出函数,测试时直接mock这个导出项即可。
首先调整Hook的代码结构:
// 抽离doSomeThing为独立函数,单独导出 export const doSomeThing = () => { // 原有逻辑 } const customHook = () => { const [count, setCount] = React.useState(0); const increment = () => { doSomeThing(); setCount((x) => x + 1 ); } return { count, increment } } export default customHook;
对应的测试代码如下:
import useCustomHook, { doSomeThing } from './customHook' // mock导出的doSomeThing方法 jest.mock('./customHook', () => ({ ...jest.requireActual('./customHook'), doSomeThing: jest.fn() })) it('触发increment时应该调用doSomeThing', () => { const { result } = renderHook(() => useCustomHook()) act(() => { result.current.increment(); }); // 直接断言doSomeThing的调用情况 expect(doSomeThing).toHaveBeenCalledTimes(1) expect(result.current.count).toBe(1) });
注意:不建议为了测试特意把
doSomeThing放到Hook的返回值中,会为了测试需求污染生产环境的对外接口,得不偿失。
内容的提问来源于stack exchange,提问作者Sai_91
相关产品推荐
相关产品推荐

