如何Mock被服务内方法调用、且被控制器触发的服务函数
如何Mock getLastPackageVersion方法?
问题背景
测试控制器中的getPackagesUpdatesToNotify函数时,始终无法Mock服务里的getLastPackageVersion方法,每次都会触发原函数的真实请求,不想使用rewire类工具。
现有代码
控制器代码(version-control.controller.ts)
import { InstalledPackages } from '../parser/parser.service'; import { getOutdatedPackages, InstalledPackageStatus, } from './version-control.service'; interface OutdatedPackages { dependencies: InstalledPackageStatus[]; devDependencies: InstalledPackageStatus[]; } export async function getPackagesUpdatesToNotify( packages: InstalledPackages, type = 'package.json', ): Promise<OutdatedPackages> { return getOutdatedPackages(packages.dependencies, type); }
服务代码(version-control.service.ts)
import { fetch } from "../common/http.service"; export async function getLastPackageVersion( packageName: string ): Promise<VersionType> { const url = `https://registry.npmjs.org/-/package/${packageName}/dist-tags`; return await (<Promise<VersionType>>fetch(url)); } export async function getOutdatedPackages( installedPackages: PackagesVersion, type: string ): Promise<InstalledPackageStatus[]> { return Promise.all( Object.keys(installedPackages).map(async (packageName) => getLastPackageVersion(packageName) ) ); }
尝试过的方案
方案1:使用jest.spyOn
import * as myService from './my.service'; it('my test', async () => { const getLastPackageVersionSpy = jest.spyOn(myService, 'getLastPackageVersion').mockReturnValue( Promise.resolve(42), await getPackagesUpdatesToNotify(packages, type) });
方案2:使用jest.mock
import { getLastPackageVersion } from './my.service'; import { getPackagesUpdatesToNotify } from './version-control.controller'; jest.mock('./myse.service', () => ({ ...jest.requireActual('./myse.service'), getLastPackageVersion: jest.fn(), })); it('my test', async () => { (getLastPackageVersion as jest.Mock).mockResolvedValue( Promise.resolve(42), ); await getPackagesUpdatesToNotify(packages, type) });
问题原因
核心问题是模块内部直接调用的函数无法被外部Mock:服务中的getOutdatedPackages是直接调用同文件内的getLastPackageVersion函数本身,而非通过模块导出的引用调用。jest的Mock机制是替换模块导出对象上的方法引用,无法拦截这种内部直接调用。
正确解决方案
方案1:修改服务代码,通过模块引用调用内部函数
修改服务代码,让内部调用通过模块自身的导出引用,这样jest就能拦截到调用:
import { fetch } from "../common/http.service"; import * as self from './version-control.service'; // 导入自身模块 export async function getLastPackageVersion( packageName: string ): Promise<VersionType> { const url = `https://registry.npmjs.org/-/package/${packageName}/dist-tags`; return await (<Promise<VersionType>>fetch(url)); } export async function getOutdatedPackages( installedPackages: PackagesVersion, type: string ): Promise<InstalledPackageStatus[]> { return Promise.all( Object.keys(installedPackages).map(async (packageName) => self.getLastPackageVersion(packageName) // 通过模块引用调用 ) ); }
对应的测试代码:
import * as versionControlService from './version-control.service'; import { getPackagesUpdatesToNotify } from './version-control.controller'; it('should mock getLastPackageVersion correctly', async () => { // Mock getLastPackageVersion,替换为符合VersionType结构的返回值 const mockSpy = jest.spyOn(versionControlService, 'getLastPackageVersion') .mockResolvedValue({ latest: '1.0.0' }); const mockPackages = { dependencies: { 'test-pkg': '0.1.0' } } as InstalledPackages; await getPackagesUpdatesToNotify(mockPackages); // 验证Mock被调用 expect(mockSpy).toHaveBeenCalledTimes(1); expect(mockSpy).toHaveBeenCalledWith('test-pkg'); // 测试后恢复原函数 mockSpy.mockRestore(); });
方案2:直接Mock控制器依赖的getOutdatedPackages函数
由于控制器直接调用的是getOutdatedPackages,可以跳过内部的getLastPackageVersion,直接Mock这个上层函数,更简洁:
import * as versionControlService from './version-control.service'; import { getPackagesUpdatesToNotify } from './version-control.controller'; it('should return outdated packages via mocked getOutdatedPackages', async () => { const mockOutdatedPackages: InstalledPackageStatus[] = [ { name: 'test-pkg', current: '0.1.0', latest: '1.0.0' } ]; // Mock getOutdatedPackages jest.spyOn(versionControlService, 'getOutdatedPackages') .mockResolvedValue(mockOutdatedPackages); const mockPackages = { dependencies: { 'test-pkg': '0.1.0' } } as InstalledPackages; const result = await getPackagesUpdatesToNotify(mockPackages); // 验证返回结果符合预期 expect(result.dependencies).toEqual(mockOutdatedPackages); });
内容的提问来源于stack exchange,提问作者Splinteer
相关产品推荐
相关产品推荐

