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

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();
  });
});

说明

  1. 使用jasmine.createSpyObj创建模拟对象,替代真实的OktaAuth实例,避免依赖外部服务。
  2. 通过{ provide: OKTA_AUTH, useValue: mockOktaAuth }配置提供者,匹配组件中@Inject(OKTA_AUTH)的注入令牌。
  3. 可以通过Spy对象的and.returnValue模拟不同场景的返回值,覆盖组件的各种逻辑分支。

内容的提问来源于stack exchange,提问作者Jry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 18:45:11