Angular单元测试报错:Cannot read property 'getUHToken' of undefined求助
问题排查:TypeError: Cannot read property 'getUHToken' of undefined
错误信息
TypeError: Cannot read property 'getUHToken' of undefined at getTokenAndPicture (src/app/shared/sidenav/sidenav.component.ts:31:17) at SidenavComponent.call [as ngOnInit] (src/app/shared/sidenav/sidenav.component.ts:56:3) at callHook (node_modules/@angular/core/fesm2020/core.mjs:2498:22) at callHooks (node_modules/@angular/core/fesm2020/core.mjs:2467:17) at executeInitAndCheckHooks (node_modules/@angular/core/fesm2020/core.mjs:2418:9) at refreshView (node_modules/@angular/core/fesm2020/core.mjs:11992:21) at renderComponentOrTemplate (node_modules/@angular/core/fesm2020/core.mjs:12094:9) at tickRootContext (node_modules/@angular/core/fesm2020/core.mjs:13215:13) at detectChangesInRootView (node_modules/@angular/core/fesm2020/core.mjs:13241:5) at RootViewRef.detectChanges (node_modules/@angular/core/fesm2020/core.mjs:13757:9)
代码片段
组件TS代码
public constructor(private router: Router, private userService: UserService, private rebarAuthService: RebarAuthService, private auth: MsalService, private picture: PictureService) { this.user = this.userService; } public ngOnInit() { let retries = 0; const retryLimit = 3; const getTokenAndPicture = () => { this.picture.getUHToken().subscribe({ next: result => { sessionStorage.setItem("token", result.accessToken); this.picture.getPicture(sessionStorage.getItem("token"), this.userService.enterpriseId).subscribe( response => { if (response) { this.profile = 'data:image/jpg;base64,' + response; this.isImgProfileLoaded = true; } }, error => { if (error.status === 401) { if (retries < retryLimit) { retries++; getTokenAndPicture(); } else { console.log("Maximum retry limit reached."); } } } ); } }); }; getTokenAndPicture();
测试SPEC代码
describe('SidenavComponent', () => { let component: SidenavComponent; let fixture: ComponentFixture<SidenavComponent>; let service: MsalService; let publicClientApplication: PublicClientApplication; let pictureService: PictureService; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ MaterialModule, RouterTestingModule, HttpClientTestingModule ], declarations: [SidenavComponent], providers: [ { provide: UserService, useValue: mockUserService }, { provide: MsalService, useValue: service }, { provide: MSAL_INSTANCE, useValue: publicClientApplication }, { provide: PictureService, useValue: pictureService }, AppConfigService, { provide: APP_CONFIGURATION, useValue: mockConfiguration }, { provide: RebarAuthService, useValue: new MockRebarAuthService() } ] }).compileComponents(); service = TestBed.inject(MsalService); publicClientApplication = TestBed.inject(MSAL_INSTANCE) as unknown as PublicClientApplication; }));
问题原因
测试代码中,pictureService、service(MsalService)、publicClientApplication变量仅声明但未初始化,值为undefined。在TestBed配置的providers中,直接将这些未初始化的变量作为useValue传入,导致组件注入的this.picture等依赖为undefined,调用this.picture.getUHToken()时触发错误。
解决方案
步骤1:创建各依赖的Mock实例
在测试代码开头,为需要注入的服务创建模拟对象,包含组件调用的方法:
import { of } from 'rxjs'; import { jasmine } from '@angular/core/testing'; // 模拟PictureService const mockPictureService = { getUHToken: jasmine.createSpy('getUHToken').and.returnValue(of({ accessToken: 'mock-token' })), getPicture: jasmine.createSpy('getPicture').and.returnValue(of('mock-base64-image-data')) }; // 模拟MsalService const mockMsalService = jasmine.createSpyObj('MsalService', ['loginPopup', 'logout']); // 模拟PublicClientApplication const mockPublicClientApplication = jasmine.createSpyObj('PublicClientApplication', ['acquireTokenSilent']);
步骤2:修正TestBed的Providers配置
将providers中的useValue替换为上述创建的mock实例:
beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ MaterialModule, RouterTestingModule, HttpClientTestingModule ], declarations: [SidenavComponent], providers: [ { provide: UserService, useValue: mockUserService }, { provide: MsalService, useValue: mockMsalService }, // 使用mock实例 { provide: MSAL_INSTANCE, useValue: mockPublicClientApplication }, // 使用mock实例 { provide: PictureService, useValue: mockPictureService }, // 使用mock实例 AppConfigService, { provide: APP_CONFIGURATION, useValue: mockConfiguration }, { provide: RebarAuthService, useValue: new MockRebarAuthService() } ] }).compileComponents(); service = TestBed.inject(MsalService); publicClientApplication = TestBed.inject(MSAL_INSTANCE) as unknown as PublicClientApplication; }));
步骤3:按需调整Mock返回值
如果需要测试错误分支(比如401重试逻辑),可以修改mock方法的返回值为错误的Observable:
import { throwError } from 'rxjs'; // 模拟getUHToken返回401错误 mockPictureService.getUHToken.and.returnValue(throwError({ status: 401 }));
内容的提问来源于stack exchange,提问作者Kenzo_Gilead
相关产品推荐
相关产品推荐

