Mock OktaAuthService in Angular 12

In the posting at Okta Auth JS and Angular | Okta Developer you provide examples of how to use OktaAuthService in Angular. Could someone please provide some tests showing how you mock this in components and services tests please?

Here’s an example test that mocks the OktaAuthModule:

import { TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { OKTA_CONFIG, OktaAuthModule } from '@okta/okta-angular';

describe('AppComponent', () => {
  const oktaConfig = {
    issuer: 'https://not-real.okta.com',
    clientId: 'fake-client-id',
    redirectUri: 'http://localhost:4200'
  };

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        OktaAuthModule
      ],
      declarations: [
        AppComponent
      ],
      providers: [{provide: OKTA_CONFIG, useValue: oktaConfig}]
    }).compileComponents();
  }));

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`should have as title 'ng-demo'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('ng-demo');
  });

  it('should render title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('.content span').textContent).toContain('ng-demo app is running!');
  });
});

That helps with how to setup the inject token but OktaAuthService needs a mock implementation maybe?

This has some answers: okta-angular/service.test.ts at master · okta/okta-angular · GitHub

You should be able to use HttpTestingController for this. I don’t have an example for OktaAuthService, but here’s one for a SearchService. First of all, here’s the service:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class SearchService {

  constructor(private http: HttpClient) { }

  getAll() {
    return this.http.get('assets/data/people.json');
  }

  search(q: string): Observable<any> {
    if (!q || q === '*') {
      q = '';
    } else {
      q = q.toLowerCase();
    }
    return this.getAll().pipe(
      map((data: any) => data
        .map((item: Person) => !!localStorage['person' + item.id] ?
          JSON.parse(localStorage['person' + item.id]) : item)
        .filter((item: Person) => JSON.stringify(item).toLowerCase().includes(q))
      ));
  }

  get(id: number) {
    return this.getAll().pipe(map((all: any) => {
      if (localStorage['person' + id]) {
        return JSON.parse(localStorage['person' + id]);
      }
      return all.find((e: Person) => e.id === id);
    }));
  }

  save(person: Person) {
    localStorage['person' + person.id] = JSON.stringify(person);
  }
}

export class Address {
  street: string;
  city: string;
  state: string;
  zip: string;

  constructor(obj?: any) {
    this.street = obj && obj.street || null;
    this.city = obj && obj.city || null;
    this.state = obj && obj.state || null;
    this.zip = obj && obj.zip || null;
  }
}

export class Person {
  id: number;
  name: string;
  phone: string;
  address: Address;

  constructor(obj?: any) {
    this.id = obj && Number(obj.id) || null;
    this.name = obj && obj.name || null;
    this.phone = obj && obj.phone || null;
    this.address = obj && obj.address || null;
  }
}

And here’s its test that uses HttpTestingController to mock the response.

import { TestBed } from '@angular/core/testing';
import { SearchService } from './search.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

describe('SearchService', () => {
  let service: SearchService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [SearchService]
    });

    service = TestBed.inject(SearchService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should retrieve all search results', () => {
    const mockResponse = [
      {name: 'Nikola Jokić'},
      {name: 'Mike Malone'}
    ];

    service.getAll().subscribe((people: any) => {
      expect(people.length).toBe(2);
      expect(people[0].name).toBe('Nikola Jokić');
      expect(people).toEqual(mockResponse);
    });

    const req = httpMock.expectOne('assets/data/people.json');
    expect(req.request.method).toBe('GET');
    req.flush(mockResponse);
  });

  it('should filter by search term', () => {
    const mockResponse = [{name: 'Nikola Jokić'}];

    service.search('nik').subscribe((people: any) => {
      expect(people.length).toBe(1);
      expect(people[0].name).toBe('Nikola Jokić');
    });

    const req = httpMock.expectOne('assets/data/people.json');
    expect(req.request.method).toBe('GET');
    req.flush(mockResponse);
  });

  it('should fetch by id', () => {
    const mockResponse = [
      {id: 1, name: 'Nikola Jokić'},
      {id: 2, name: 'Mike Malone'}
    ];

    service.get(2).subscribe((person: any) => {
      expect(person.name).toBe('Mike Malone');
    });

    const req = httpMock.expectOne('assets/data/people.json');
    expect(req.request.method).toBe('GET');
    req.flush(mockResponse);
  });

  afterEach(() => {
    httpMock.verify();
  });
});