Testing the private method call in the constructor
Isolated unit tests are considered best practice when testing a service by the Angular Testing Guide, i.e. there is no need for Angular testing utilities.
We cannot test that a method was called from the constructor by spying on an object instance, as the method has already been called once we have a reference to the instance.
Instead, we need to spy on the prototype of the service (thanks, Dave Newton!). When creating methods on a JavaScript class
, we are actually creating the methods on the <ClassName>.prototype
.
Given this factory for NgZone spies that is based on MockNgZone from Angular testing internals:
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 mock the NgZone dependency to isolate the service in our tests and even describe the outgoing commands that are 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);
});
});
});
Normally we would not want to test private methods, but instead observe the public side effects that it makes.
However, we can use Jasmine Spies to achieve what we want.
See full example on StackBlitz
The Angular Service Lifecycle
See examples that demonstrate the Angular Service Lifecycle on StackBlitz. Do read the comments in the hello.*.ts
files and open your JavaScript console to see the messages that are output.
Create an Angular StackBlitz with Jasmine tests
Fork my StackBlitz to test Angular with Jasmine just like in this answer