在构造函数中测试私有方法调用
Isolated unit tests 被视为best practice when testing a service by the Angular Testing Guide,即不需要 Angular 测试实用程序。
我们无法通过监视对象实例来测试构造函数是否调用了方法,因为一旦我们引用了该实例,该方法就已经被调用了。
相反,我们需要监视服务的原型 (thanks, Dave Newton!)。在 JavaScript class 上创建方法时,我们实际上是在 <ClassName>.prototype 上创建方法。
鉴于这个基于MockNgZone from Angular testing internals 的 NgZone 间谍工厂:
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;
}
我们可以模拟 NgZone 依赖项以在我们的测试中隔离服务,甚至描述在区域外运行的传出命令。
// 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);
});
});
});
通常我们不想测试私有方法,而是观察它产生的公共副作用。
但是,我们可以使用Jasmine Spies 来实现我们想要的。
See full example on StackBlitz
Angular 服务生命周期
See examples that demonstrate the Angular Service Lifecycle on StackBlitz。请阅读 hello.*.ts 文件中的 cmets 并打开 JavaScript 控制台以查看输出的消息。
使用 Jasmine 测试创建 Angular StackBlitz
Fork my StackBlitz 用 Jasmine 测试 Angular,就像在这个答案中一样