Testing a private method call in the constructor
Isolated unit tests are considered best practice when testing a service using the Angular Testing Guide , meaning there is no need for Angular testing utilities.
We cannot check if the method is called from the constructor by spying on the instance of the object, since this method was already called when we have a reference to the instance.
Instead, we need to look into the prototype of the service ( thanks, Dave Newton! ). When creating methods in the JavaScript class we actually create methods on <ClassName>.prototype .
Given this factory for NgZone spies, based on the MockNgZone of Angular testing internal components :
import { EventEmitter, NgZone } from '@angular/core'; export function createNgZoneSpy(): NgZone { const spy = jasmine.createSpyObj('ngZoneSpy', { onStable: new EventEmitter(false), run: (fn: Function) => fn(), runOutsideAngular: (fn: Function) => fn(), simulateZoneExit: () => { this.onStable.emit(null); }, }); return spy; }
We can make fun of the NgZone dependency in order to isolate the service in our tests and even describe outgoing commands that run outside the zone.
// Straight Jasmine - no imports from Angular test libraries import { NgZone } from '@angular/core'; import { createNgZoneSpy } from '../test/ng-zone-spy'; import { GdlService } from './gdl.service'; describe('GdlService (isolated unit tests)', () => { describe('loadApp', () => { const methodUnderTest: string = 'loadApp'; let ngZone: NgZone; let service: GdlService; beforeEach(() => { spyOn<any>(GdlService.prototype, methodUnderTest).and.callThrough(); ngZone = createNgZoneSpy(); service = new GdlService(ngZone); }); it('loads the app once when initialized', () => { expect(GdlService.prototype[methodUnderTest]).toHaveBeenCalledWith(service.appName); expect(GdlService.prototype[methodUnderTest]).toHaveBeenCalledTimes(1); }); it('runs logic outside the zone when initialized.', () => { expect(ngZone.runOutsideAngular).toHaveBeenCalledTimes(1); }); }); });
Usually we would not want to test private methods, but instead we observe the public side effects that it does.
However, we can use Jasmine Spies to achieve what we want.
See full example at StackBlitz
Angular Service Lifecycle
See examples demonstrating the Angular service life cycle on StackBlitz . Read the comments in the hello.*.ts files and open the JavaScript console to view the output messages.
Create Angular StackBlitz with Jasmine Tests
Scroll through my StackBlitz to check Angular on Jasmine, as in this answer