angulartestingdependency-injectionkarma-jasmineconstructor-injection

Angular Unit Test: testing component with constructor injection


I am trying to write unit test for the login component below. This component has a constructor with @Inject. How do we test an angular component with @Inject in constructor? Can someone help? Thanks!

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

Here is the error

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)

I must miss something. Please let me know what I am missing. Thanks in advance.

tried to import both import { OKTA_AUTH } from '@okta/okta-angular'; import { OktaAuth } from '@okta/okta-auth-js';


Solution

  • This is where I ended up finding the answer: https://github.com/okta-samples/okta-angular-quickstart/blob/main/src/app/app.component.spec.ts

    ...
    
    import { OktaAuthStateService, OKTA_AUTH } from '@okta/okta-angular';
    
    describe('AppComponent', () => {
      const authStateSpy = jasmine.createSpyObj('OktaAuthStateService', [], {
        authState$: of({
          isAuthenticated: false
        })
      });
    
      const authSpy = jasmine.createSpyObj('OktaAuth', ['login']);
    
      beforeEach(async () => {
        await TestBed.configureTestingModule({
          imports: [
            RouterTestingModule
          ],
          declarations: [
            AppComponent
          ],
          providers: [
            { provide: OktaAuthStateService, useValue: authStateSpy },
            { provide: OKTA_AUTH, useValue: authSpy } // <-- see how you can provide the token, but provide your own stub/mock?  
          ]
        }).compileComponents();
      });