Angular单元测试:如何测试含@Inject构造注入的组件?
问题:测试含@Inject构造注入的Angular组件时出现NG0204错误?
我在为LoginComponent编写单元测试时遇到NG0204错误,提示无法解析OktaAuth的所有参数。该组件通过@Inject(OKTA_AUTH)注入OktaAuth,请问如何解决这个测试问题?
LoginComponent.ts
import { Component, Inject, OnInit } from '@angular/core'; import { OKTA_AUTH } from '@okta/okta-angular'; import { OktaAuth } from '@okta/okta-auth-js'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { isAuthenticated!: boolean; userName?: string; error?: Error; constructor(@Inject(OKTA_AUTH) public oktaAuth: OktaAuth) { } async ngOnInit() { this.isAuthenticated = await this.oktaAuth.isAuthenticated(); if (this.isAuthenticated) { const userClaims = await this.oktaAuth.getUser(); this.userName = userClaims.name; } } async login() { try { await this.oktaAuth.signInWithRedirect(); } catch (err: any) { console.error(err); this.error = err; } } async logout() { await this.oktaAuth.signOut(); } }
LoginComponent.spec.ts(原错误版本)
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { OktaAuth } from '@okta/okta-auth-js'; describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent>; let authService: OktaAuth; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [LoginComponent], providers: [OktaAuth] }).compileComponents(); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; authService = TestBed.inject(OktaAuth); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
错误信息
Chrome Headless 114.0.5735.133 (Windows 10) LoginComponent should create FAILED Error: NG0204: Can't resolve all parameters for OktaAuth: (?). error properties: Object({ code: 204 }) at getUndecoratedInjectableFactory (node_modules/@angular/core/fesm2022/core.mjs:9227:15) at injectableDefOrInjectorDefFactory (node_modules/@angular/core/fesm2022/core.mjs:9217:16) at providerToFactory (node_modules/@angular/core/fesm2022/core.mjs:9263:52) at providerToRecord (node_modules/@angular/core/fesm2022/core.mjs:9247:25) at R3Injector.processProvider (node_modules/@angular/core/fesm2022/core.mjs:9144:24) at fn (node_modules/@angular/core/fesm2022/core.mjs:8975:59) at forEachSingleProvider (node_modules/@angular/core/fesm2022/core.mjs:9318:13) at forEachSingleProvider (node_modules/@angular/core/fesm2022/core.mjs:9315:13) at new R3Injector (node_modules/@angular/core/fesm2022/core.mjs:8975:9) at createInjectorWithoutInjectorInstances (node_modules/@angular/core/fesm2022/core.mjs:10591:12)
我也尝试同时导入OKTA_AUTH和OktaAuth,但问题依旧。
解决方案
问题根源
OktaAuth并非Angular原生的可注入服务,组件是通过OKTA_AUTH令牌来获取预配置的OktaAuth实例。测试中直接提供OktaAuth类,Angular无法解析其构造参数,导致NG0204错误。
正确做法
使用OKTA_AUTH作为注入令牌,提供模拟的OktaAuth实例或Spy对象,代替直接提供OktaAuth类。这样既隔离了外部依赖,又能测试组件逻辑。
修改后的测试代码
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { OKTA_AUTH } from '@okta/okta-angular'; import { OktaAuth } from '@okta/okta-auth-js'; describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent>; let mockOktaAuth: jasmine.SpyObj<OktaAuth>; beforeEach(async () => { // 创建OktaAuth的Spy对象,模拟组件用到的方法 mockOktaAuth = jasmine.createSpyObj('OktaAuth', [ 'isAuthenticated', 'getUser', 'signInWithRedirect', 'signOut' ]); await TestBed.configureTestingModule({ declarations: [LoginComponent], providers: [ // 通过OKTA_AUTH令牌提供模拟对象 { provide: OKTA_AUTH, useValue: mockOktaAuth } ] }).compileComponents(); fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); // 扩展测试:验证ngOnInit逻辑 it('should set auth state and username when user is authenticated', async () => { // 模拟异步方法的返回值 mockOktaAuth.isAuthenticated.and.returnValue(Promise.resolve(true)); mockOktaAuth.getUser.and.returnValue(Promise.resolve({ name: 'Test User' })); await component.ngOnInit(); expect(component.isAuthenticated).toBeTrue(); expect(component.userName).toBe('Test User'); expect(mockOktaAuth.isAuthenticated).toHaveBeenCalled(); expect(mockOktaAuth.getUser).toHaveBeenCalled(); }); });
说明
- 使用
jasmine.createSpyObj创建模拟对象,替代真实的OktaAuth实例,避免依赖外部服务。 - 通过
{ provide: OKTA_AUTH, useValue: mockOktaAuth }配置提供者,匹配组件中@Inject(OKTA_AUTH)的注入令牌。 - 可以通过Spy对象的
and.returnValue模拟不同场景的返回值,覆盖组件的各种逻辑分支。
内容的提问来源于stack exchange,提问作者Jry
相关产品推荐
相关产品推荐

