You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:22:52