使用Jest Mock React Hooks时两个测试用例均失败,简化代码后仍不通过
测试全部失败的核心原因
你碰到的是绝大多数人第一次用Jest Mock React Hooks都会踩的ES模块导入绑定陷阱,和你写的测试逻辑本身没关系,是导入和Mock的引用链路根本没打通。
为什么你的代码完全匹配不上
- 你在业务文件
myFileToTest.js里用了ES模块的静态命名导入:import { useContext, useState } from 'react'
这种导入方式有两个关键特性:- 绑定是在模块加载的最早期完成的,会直接把react模块导出的
useContext、useState的内存引用,绑定到当前业务文件的作用域下 - 这个绑定是只读的,不会随后续对react导出对象的属性修改而变化
- 绑定是在模块加载的最早期完成的,会直接把react模块导出的
- 具体对应两个测试的失败逻辑:
- 第一个测试里,你给
React.useContext重新赋值成了jest mock,但这个修改是作用在你测试文件拿到的React默认导出对象上的,你的业务文件里的useContext变量,根本不会读取这个被改过的对象,它存的还是最开始加载时拿到的原始useContext引用,两个引用根本不是同一个,toBe的严格相等断言自然失败。 - 第二个测试的逻辑完全一样:你用
jest.spyOn给React.useState加的spy实例,和业务文件里绑定的原始useState根本不是同一个引用,比对必然不通过。
- 第一个测试里,你给
可正常运行的Mock写法
不要硬修改React对象上的Hook属性,直接通过Jest的模块mock能力替换react模块本身的导出,这样模块加载阶段绑定的引用就是你设置的mock:
- 测试文件顶部直接声明对react模块的mock,保留原有功能只替换需要mock的Hook:
jest.mock('react', () => { const originalReact = jest.requireActual('react'); return { ...originalReact, useContext: jest.fn(), useState: jest.fn(), } })
- 后续测试里直接从react导入对应的mock Hook引用做断言即可:
import React, { useContext, useState } from 'react'; import { returnsUseContext, returnsUseState } from './myFileToTest'; describe('Hook引用匹配测试', () => { afterEach(() => jest.clearAllMocks()); it('匹配useContext的引用', () => { expect(returnsUseContext()).toBe(useContext); }) it('匹配useState的引用', () => { expect(returnsUseState()).toBe(useState); }) })
额外避坑提示
- 如果你用的是CommonJS的导入写法(比如
const { useState } = require('react')),你原来那种给React.useState赋值的写法是能生效的,但ES模块的静态导入因为有只读绑定的特性,这种写法100%无法命中。 - 不需要手动在
beforeEach里存原始Hook再赋值还原,Jest的模块mock机制会在每个测试环境初始化时自动隔离mock状态,配合jest.clearAllMocks()就能完全避免跨测试用例的状态污染。
内容的提问来源于stack exchange,提问作者Raffaele
相关产品推荐
相关产品推荐

