如何Mock含Signal的Angular类?Jest测试遇Getter识别问题
如何Mock返回Signal的Angular Store Getter并验证访问情况
我在Angular应用中用Jest + auto-jest-spies测试依赖其他服务的组件/守卫(比如PermissionGuard),需要Mock基于signalstory实现的AccountStore类。这个Store的isLoggedIn getter返回一个computed Signal,但用createSpyFromClass指定gettersToSpyOn后,无法正确Mock这个getter,也不知道怎么验证Signal是否被访问。
核心问题分析
isLoggedIn是返回Signal函数的getter,而auto-jest-spies默认处理的是返回普通值的getter,无法直接识别并模拟Signal的可调用特性。要解决这个问题,需要让mock的getter返回一个可跟踪的mock函数(模拟Signal的行为),同时验证这个函数的调用情况。
方案1:手动Mock Getter并模拟Signal行为
直接创建mock对象,将isLoggedIn定义为Jest mock函数,完全模拟Signal的调用逻辑,同时支持访问验证。
describe('PermissionGuard', () => { let storeMock: Partial<AccountStore> & { isLoggedIn: jest.Mock; }; beforeEach(() => { // 创建模拟Signal的mock函数 const mockIsLoggedInSignal = jest.fn(() => false); storeMock = { isLoggedIn: mockIsLoggedInSignal, // 按需mock其他属性/方法,比如userInfo userInfo: jest.fn(() => ({ userId: null })) }; TestBed.configureTestingModule({ providers: [ { provide: AccountStore, useValue: storeMock }, PermissionGuard, ], }).compileComponents(); }); it('should deny access when user is not logged in', async () => { // Arrange:设置Signal返回值 storeMock.isLoggedIn.mockReturnValue(false); // Act:执行守卫逻辑 const canActivate = await TestBed.inject(PermissionGuard).canActivate(...); // Assert:验证结果和Signal访问情况 expect(canActivate).toBe(false); expect(storeMock.isLoggedIn).toHaveBeenCalled(); // 验证Signal被调用(访问) }); it('should allow access when user is logged in', async () => { // Arrange storeMock.isLoggedIn.mockReturnValue(true); // Act const canActivate = await TestBed.inject(PermissionGuard).canActivate(...); // Assert expect(canActivate).toBe(true); expect(storeMock.isLoggedIn).toHaveBeenCalled(); }); });
方案2:结合auto-jest-spies调整Getter Mock
如果想保留auto-jest-spies生成的其他spy功能,可以手动覆盖getter的实现,让它返回一个mock Signal:
import { createSpyFromClass, Spy } from 'auto-jest-spies'; describe('PermissionGuard', () => { let storeMock!: Spy<AccountStore>; let mockIsLoggedInSignal: jest.Mock; beforeEach(() => { mockIsLoggedInSignal = jest.fn(() => false); storeMock = createSpyFromClass(AccountStore, { gettersToSpyOn: ['isLoggedIn'] }); // 重新定义getter,让它返回mock Signal Object.defineProperty(storeMock, 'isLoggedIn', { get: () => mockIsLoggedInSignal, configurable: true }); TestBed.configureTestingModule({ providers: [ { provide: AccountStore, useValue: storeMock }, PermissionGuard, ], }).compileComponents(); }); it('should test user access correctly', async () => { // Arrange mockIsLoggedInSignal.mockReturnValue(true); // Act const result = await TestBed.inject(PermissionGuard).canActivate(...); // Assert expect(result).toBe(true); expect(mockIsLoggedInSignal).toHaveBeenCalled(); // 验证Signal被访问 }); });
方案3:Mock Store State(贴近真实逻辑)
如果需要模拟state()的返回值,让isLoggedIn的computed Signal基于真实状态计算,可以直接mockstate方法:
describe('PermissionGuard', () => { let storeMock: Partial<AccountStore>; beforeEach(() => { storeMock = { // mock state方法,返回不同状态 state: jest.fn(() => ({ userId: null })), // 模拟isLoggedIn的computed逻辑 get isLoggedIn() { return jest.fn(() => !!this.state().userId); } }; TestBed.configureTestingModule({ providers: [ { provide: AccountStore, useValue: storeMock }, PermissionGuard, ], }).compileComponents(); }); it('should deny access when userId is null', async () => { // Arrange:设置state返回无userId的状态 (storeMock.state as jest.Mock).mockReturnValue({ userId: null }); // Act const canActivate = await TestBed.inject(PermissionGuard).canActivate(...); // Assert expect(canActivate).toBe(false); expect(storeMock.state).toHaveBeenCalled(); expect(storeMock.isLoggedIn()).toBe(false); }); it('should allow access when userId exists', async () => { // Arrange:设置state返回有userId的状态 (storeMock.state as jest.Mock).mockReturnValue({ userId: '123' }); // Act const canActivate = await TestBed.inject(PermissionGuard).canActivate(...); // Assert expect(canActivate).toBe(true); expect(storeMock.state).toHaveBeenCalled(); expect(storeMock.isLoggedIn()).toBe(true); }); });
关键总结
- Signal本质是可调用函数,mock返回Signal的getter时,必须让getter返回一个mock函数
- 验证Signal是否被访问,等价于验证这个mock函数是否被调用(
toHaveBeenCalled()) - 三种方案可根据测试场景选择:手动mock灵活性最高,结合auto-jest-spies保留原有spy功能,mock state更贴近真实业务逻辑
内容的提问来源于stack exchange,提问作者J4N
相关产品推荐
相关产品推荐

