为何Vitest/Jest中被Mock的JS导入仍有非零代码覆盖率?
问题原因与解决方案
问题根源
你遇到的情况是Vitest自动Mock机制的特性导致的:当你调用vi.mock('./service')但未提供自定义Mock工厂函数时,Vitest会自动生成一个与原始模块结构匹配的Mock。为了匹配结构,它会加载原始的service.js模块来解析导出内容(比如确认这是一个类),这个加载过程会执行原始模块的类定义代码(类声明、类的大括号部分),因此这部分代码被统计进了覆盖率;而类的serviceFn方法因为被你用vi.spyOn替换了实现,没有执行原始代码,所以显示未覆盖。
你的Mock本身是生效的(测试能通过证明这一点),只是覆盖率统计到了Vitest自动Mock时加载原始模块的额外代码。
解决方案
方案1:手动编写完全Mock,避免加载原始模块
修改测试文件中的vi.mock逻辑,手动提供Mock工厂函数,完全替换原始模块的实现,这样Vitest就不会加载原始的service.js:
import { describe, expect, it, vi } from 'vitest'; import { indexFn } from './index'; import Service from './service'; // 手动Mock整个Service类,完全替代原始实现 vi.mock('./service', () => ({ default: vi.fn().mockImplementation(() => ({ serviceFn: vi.fn() })) })); describe('suite', () => { it('tests', () => { const mockedReturn = 'mockedReturn'; vi.spyOn(Service.prototype, 'serviceFn').mockReturnValue(mockedReturn); expect(indexFn()).toBe(mockedReturn); }); });
修改后,原始service.js不会被加载,覆盖率报告中就不会出现它的统计数据。
方案2:在覆盖率配置中排除目标文件
如果你不想修改测试代码,可以在Vitest配置文件(如vitest.config.js)中设置覆盖率排除规则,忽略service.js:
import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { coverage: { exclude: [ 'service.js' // 排除该文件的覆盖率统计 ] } } });
也可以更精确地指定只统计需要测试的文件:
coverage: { include: [ 'index.js' ] }
内容的提问来源于stack exchange,提问作者Stanley Clark
相关产品推荐
相关产品推荐

