如何在Jest中Mock项目上下文VSS库并解决类型错误
解决Jest测试中VSS库的Mock问题
问题描述
我在.ts文件中编写了Jest测试代码,已通过jest.spyOn Mock了appUtils中的getHostName等方法,但appUtils内部依赖项目上下文的VSS库(代码中通过const VSS_SDK = VSS;引用),运行测试时出现“ReferenceError: VSS is not defined”错误。尝试Mock VSS后,又触发类型错误:Type '() => { host: { authority: string; }; }' is not assignable to type '() => WebContext',提示WebContext缺少account等属性。
测试代码:
import { getBuildUrl } from '../../service/navigationService'; const appUtils = require('../../utils/appUtils'); describe('Report Title suite tests', () => { let getHostNameStub = jest.spyOn(appUtils, 'getHostName') let getOrganizationNameStub = jest.spyOn(appUtils, 'getOrganizationName') let getProjectNameStub = jest.spyOn(appUtils, 'getProjectName') describe('returnBuildUrl', () => { getHostNameStub.mockReturnValue("HostNametest") getOrganizationNameStub.mockReturnValue("OrganizationTest") getProjectNameStub.mockReturnValue("ProjectNameTest") it('should display title and description without report URL', async () => { expect(getBuildUrl("IdTest")).toBeDefined(); expect(getBuildUrl("IdTest")).toEqual("https://HostNametest/OrganizationTest/ProjectNameTest/_build/results?buildId=IdTest") }); }); });
appUtils相关代码:
const VSS_SDK = VSS; /** * return host name */ export function getHostName() { return VSS_SDK.getWebContext().host.authority; }
WebContext接口定义:
interface WebContext { account: HostContext; /** * Information about the Collection used in the current request (may be null) */ collection: HostContext; /** * Information about the current request context's host */ host: ExtendedHostContext; /** * Information about the project used in the current request (may be null) */ project: ContextIdentifier; /** * Information about the team used in the current request (may be null) */ team: TeamContext; /** * Information about the current user */ user: UserContext; }
解决方案
方案一:完善全局VSS Mock,满足TypeScript类型要求
TypeScript会严格校验WebContext的结构,所以Mock的VSS对象必须返回完整符合接口定义的结构(缺失属性可以用空对象或默认值填充):
- 在测试文件顶部先声明全局VSS的类型,再Mock完整结构:
// 声明全局VSS类型,避免TS报错 declare global { var VSS: { getWebContext: () => WebContext; }; } // Mock符合WebContext接口的完整结构,缺失属性用空对象占位 global.VSS = { getWebContext: () => ({ account: {} as HostContext, collection: {} as HostContext, host: { authority: "HostNametest" } as ExtendedHostContext, project: {} as ContextIdentifier, team: {} as TeamContext, user: {} as UserContext, }), }; // 注意:要在Mock之后再导入依赖模块 import { getBuildUrl } from '../../service/navigationService'; const appUtils = require('../../utils/appUtils');
这样既解决了VSS is not defined的报错,又能通过TypeScript的类型校验。
方案二:优化Mock逻辑,完全绕过VSS依赖
既然已经打算MockappUtils的方法,直接让这些Mock方法返回预期值即可,根本不需要触发内部的VSS调用,有两种实现方式:
方式1:调整Spy创建时机
确保在模块导入前就创建Spy,避免VSS代码被执行:
// 先导入appUtils并创建Spy const appUtils = require('../../utils/appUtils'); const getHostNameStub = jest.spyOn(appUtils, 'getHostName').mockReturnValue("HostNametest"); const getOrganizationNameStub = jest.spyOn(appUtils, 'getOrganizationName').mockReturnValue("OrganizationTest"); const getProjectNameStub = jest.spyOn(appUtils, 'getProjectName').mockReturnValue("ProjectNameTest"); // 再导入测试目标模块 import { getBuildUrl } from '../../service/navigationService'; describe('Report Title suite tests', () => { describe('returnBuildUrl', () => { it('should display title and description without report URL', async () => { expect(getBuildUrl("IdTest")).toBeDefined(); expect(getBuildUrl("IdTest")).toEqual("https://HostNametest/OrganizationTest/ProjectNameTest/_build/results?buildId=IdTest"); }); }); // 测试完成后恢复原方法 afterAll(() => { getHostNameStub.mockRestore(); getOrganizationNameStub.mockRestore(); getProjectNameStub.mockRestore(); }); });
方式2:直接Mock整个appUtils模块
用jest.mock直接替换整个appUtils模块,彻底隔离VSS依赖:
import { getBuildUrl } from '../../service/navigationService'; // 直接Mock整个appUtils模块,指定方法返回值 jest.mock('../../utils/appUtils', () => ({ getHostName: jest.fn().mockReturnValue("HostNametest"), getOrganizationName: jest.fn().mockReturnValue("OrganizationTest"), getProjectName: jest.fn().mockReturnValue("ProjectNameTest"), })); describe('Report Title suite tests', () => { describe('returnBuildUrl', () => { it('should display title and description without report URL', async () => { expect(getBuildUrl("IdTest")).toBeDefined(); expect(getBuildUrl("IdTest")).toEqual("https://HostNametest/OrganizationTest/ProjectNameTest/_build/results?buildId=IdTest"); }); }); });
这种方式更简洁,完全不会触发appUtils内部的VSS相关代码,自然不会出现任何VSS相关的错误。
内容的提问来源于stack exchange,提问作者rasilvap
相关产品推荐
相关产品推荐

