Jest中Mock注入的HTTP类实例方法失效问题及最佳实践咨询
Jest中Mock注入的HTTP类实例方法失效问题及最佳实践咨询
嗨,我来帮你分析下问题所在,以及给出几种靠谱的解决办法~
为什么你的Mock没生效?
看你的报错信息,代码还是走到了http.request里的fetch调用,说明你Mock的http.prototype.get并没有真正替换掉原始的get方法。可能的原因有这几点:
- 直接修改类原型的方式容易受Jest的模块隔离/缓存机制干扰,导致Mock没有覆盖到Core实际使用的那个类方法;
- 虽然Typescript类方法默认挂载在原型上,但偶尔会因为编译选项或其他因素,导致Mock没有正确绑定到实例的方法上。
不过更关键的是:既然你已经用了依赖注入的设计(Core的构造函数接收http实例),完全可以用更简单、更可靠的方式来Mock依赖,没必要去碰原型链~
方案一:直接注入Mock的HTTP实例(推荐)
这是最符合依赖注入设计初衷的方式——直接给Core传一个我们自己造的Mock实例,完全绕开原始的http类,自然不会有调用原始方法的问题。
修改后的测试代码如下:
import { Core } from '.' import http from '../http' import type { IDomainNameEntry } from './types' // 替换成你实际的类型导入路径 it('Domain Name Entry Test', async () => { // 1. 造一个Mock的http实例,完全自定义get方法的返回 const mockHttp = { get: jest.fn(async () => Promise.resolve({ ok: true, status: 200, json: async () => ({ name: 'javierhersan.stw', urls: [], ips: [] } as IDomainNameEntry), } as Response) ) } as unknown as http; // 类型断言满足Core的参数类型要求 // 2. 把Mock实例注入Core const core = new Core(mockHttp); const domainNameEntry = await core.getUserDomainNameEntry( 'javierhersan.stw', 'http://localhost:3000', ) // 3. 验证逻辑和调用 // 先确认http.get被调用了正确的URL expect(mockHttp.get).toHaveBeenCalledWith( 'http://localhost:3000/api/v1/dns/search/username/javierhersan.stw' ); // 再验证返回结果符合预期 expect(domainNameEntry).toBeDefined(); if (domainNameEntry) { expect(domainNameEntry.name).toBe('javierhersan.stw'); expect(domainNameEntry.urls).toEqual([]); expect(domainNameEntry.ips).toEqual([]); } })
方案二:用Jest Mock整个HTTP模块
如果你希望Mock整个http模块的导出(而不是单个实例),可以用Jest的jest.mock语法来批量Mock:
import { Core } from '.' import http from '../http' // 全局Mock整个http模块,让它返回一个会生成Mock实例的构造函数 jest.mock('../http', () => { return jest.fn().mockImplementation(() => ({ get: jest.fn(async () => Promise.resolve({ ok: true, status: 200, json: async () => ({ name: 'javierhersan.stw', urls: [], ips: [] }), } as Response) ) })) }) it('Domain Name Entry Test', async () => { const core = new Core(new http()); const domainNameEntry = await core.getUserDomainNameEntry( 'javierhersan.stw', 'http://localhost:3000', ) // 验证http实例的get方法被正确调用 const mockHttpInstance = http.mock.instances[0]; expect(mockHttpInstance.get).toHaveBeenCalledWith( 'http://localhost:3000/api/v1/dns/search/username/javierhersan.stw' ); // 业务逻辑断言 expect(domainNameEntry).toBeDefined(); if (domainNameEntry) { expect(domainNameEntry).toHaveProperty('name'); expect(domainNameEntry).toHaveProperty('urls'); expect(domainNameEntry).toHaveProperty('ips'); } })
额外补充:如果要Mock全局Fetch
如果你有时候需要测试原始http类的逻辑(而不是Core的业务逻辑),可以用jest-fetch-mock库全局Mock fetch API:
- 先安装依赖:
npm install jest-fetch-mock --save-dev - 在测试文件开头启用Mock:
import fetchMock from 'jest-fetch-mock' fetchMock.enableMocks() beforeEach(() => { fetchMock.resetMocks() }) it('测试http类的get方法', async () => { fetchMock.mockResponseOnce(JSON.stringify({ name: 'javierhersan.stw', urls: [], ips: [] }), { status: 200, ok: true }) const httpInstance = new http() const response = await httpInstance.get('http://test.url') const data = await response.json() expect(fetchMock).toHaveBeenCalledWith('http://test.url', { method: 'GET' }) expect(data.name).toBe('javierhersan.stw') })
总结
既然你已经用了依赖注入,优先选直接注入Mock实例的方案——代码更清晰,逻辑更直观,完全不会有原型Mock的各种坑,也完美契合依赖注入“面向抽象编程”的设计思路~
备注:内容来源于stack exchange,提问作者javierhersan
相关产品推荐
相关产品推荐

