使用Jest测试带自定义Hook的组件时Mock失效该如何解决?
自定义Hook Mock失效问题解决方法
问题根本原因
你当前的写法存在两个核心错误:
- 你将测试文件内导入的
useClickAButton挂载到了本地自定义的hooks对象上再做spy,这个本地对象和组件实际引用的./hooks/index模块没有关联,mock完全不会作用到组件实际调用的hook上 - 你调用
component.container.click()是点击整个根容器,没有命中内部的button元素,所以就算hook mock成功,点击回调也不会触发
解决方案
有两种常用的实现方式,任选其一即可:
方案1:对模块命名空间做spy
直接导入整个hook模块作为命名空间对象,再对其导出的方法做spy,即可影响组件的实际引用:
// click-a-button.test.tsx import * as React from 'react'; import {act} from "react-dom/test-utils"; import {render, screen} from "@testing-library/react"; // 导入整个hook模块作为命名空间 import * as hooks from './hooks/index' import {ClickAButton} from "./index"; test('it runs with a mocked customHook',() => { const STATE_SPY = jest.spyOn(hooks, 'useClickAButton'); const CLICK_HANDLER = jest.fn(); STATE_SPY.mockReturnValue({ handleClick: CLICK_HANDLER, total: 5, }); render(<ClickAButton />); expect(screen.getByRole('button')).toHaveTextContent('5'); act(() => { // 直接命中button元素触发点击 screen.getByRole('button').click(); }); expect(CLICK_HANDLER).toHaveBeenCalled(); // 测试结束清除mock,避免影响其他用例 STATE_SPY.mockRestore(); })
方案2:使用jest.mock全局mock模块
jest的jest.mock会被自动提升到所有导入之前执行,直接替换整个模块的导出内容:
// click-a-button.test.tsx import * as React from 'react'; import {act} from "react-dom/test-utils"; import {render, screen} from "@testing-library/react"; import {useClickAButton} from './hooks/index' import {ClickAButton} from "./index"; // 全局mockhook模块 jest.mock('./hooks/index', () => ({ useClickAButton: jest.fn() })) test('it runs with a mocked customHook',() => { const CLICK_HANDLER = jest.fn(); // 给mock方法设置返回值 (useClickAButton as jest.Mock).mockReturnValue({ handleClick: CLICK_HANDLER, total: 5, }); render(<ClickAButton />); expect(screen.getByRole('button')).toHaveTextContent('5'); act(() => { screen.getByRole('button').click(); }); expect(CLICK_HANDLER).toHaveBeenCalled(); })
内容的提问来源于stack exchange,提问作者Nicholas Ladefoged
相关产品推荐
相关产品推荐

