如何测试内部静态方法updateOrAddNew是否被调用?
如何测试事件处理器是否调用了静态方法?
问题场景
事件处理器代码(memberAdd.js):
const { Events } = require('client'); const User = require('../models/User'); module.exports = { name: Events.MemberAdd, async execute(member) { User.updateOrAddNew(member); } }
测试代码(原写法,spy未触发):
import {it, expect, vi } from 'vitest'; import User from '../models/User'; import memberAdd from './memberAdd'; it('calls updateOrAddNew on memberAdd', async () => { const spy = vi.spyOn(User, 'updateOrAddNew').mockReturnValue(true); User.updateOrAddNew = spy; const member = {}; await memberAdd.execute(member); expect(spy).toBeCalled(); });
问题:updateOrAddNew是User的静态方法,测试中spy始终未被触发。
问题原因
当测试文件中先导入memberAdd时,该模块已经加载了原始的User模块并缓存了对updateOrAddNew的引用。后续通过vi.spyOn修改的是测试文件内的User引用,并不会影响memberAdd里已存在的引用,导致spy无法捕获调用。
解决方案
方法一:提前用vi.mock模拟User模块
通过vi.mock在模块导入前就替换掉User的静态方法为mock函数,确保memberAdd使用的是模拟后的版本:
import { it, expect, vi } from 'vitest'; import memberAdd from './memberAdd'; // 模拟User模块,替换updateOrAddNew为mock函数 vi.mock('../models/User', () => ({ default: { updateOrAddNew: vi.fn().mockReturnValue(true) } })); // 导入模拟后的User import User from '../models/User'; it('calls updateOrAddNew on memberAdd', async () => { const member = {}; await memberAdd.execute(member); // 断言mock函数被调用,还可以验证传入的参数 expect(User.updateOrAddNew).toHaveBeenCalledWith(member); });
方法二:重置模块缓存后动态导入
如果不想提前mock整个模块,可以先重置模块缓存,设置spy后再动态导入memberAdd,确保它使用的是被spy替换后的User:
import { it, expect, vi } from 'vitest'; it('calls updateOrAddNew on memberAdd', async () => { // 重置所有模块缓存,清除之前的导入记录 vi.resetModules(); // 动态导入User并设置spy const UserModule = await import('../models/User'); const spy = vi.spyOn(UserModule.default, 'updateOrAddNew').mockReturnValue(true); // 动态导入memberAdd,此时它会加载最新的User引用 const memberAddModule = await import('./memberAdd'); const member = {}; await memberAddModule.default.execute(member); expect(spy).toHaveBeenCalledWith(member); });
额外注意点
- 原测试中的
User.updateOrAddNew = spy;是多余的,vi.spyOn已经自动替换目标对象上的方法为spy函数,无需手动赋值。 - 可以用
toHaveBeenCalledWith验证方法调用时的参数是否正确,比单纯的toBeCalled更严谨。
内容的提问来源于stack exchange,提问作者NicholasJohn16
相关产品推荐
相关产品推荐

