测试Angular服务是否已初始化

时间:2017-12-29 12:02:26

标签: angular typescript karma-runner karma-jasmine

我正在尝试使用Karma-Jasmine测试我的Angular服务,我需要确保在服务初始化后调用loadApp函数。测试它的最佳方法是什么?

import { Injectable, NgZone } from '@angular/core';

@Injectable()
export class GdlService {
  appName = 'myAppName';

  constructor(
    private ngZone: NgZone,
  ) {
    this.ngZone = ngZone;
    this.loadApp(this.appName);
  }


  private loadApp(appName) {
    this.ngZone.runOutsideAngular(() => {
      // ...some logic
    });
  }
}

4 个答案:

答案 0 :(得分:3)

尝试模拟ngZone的注入(我喜欢这种东西的ts-mockito),然后检查是否已经调用了ngZone.outsideOfAngular。由于打字稿的性质,我不认为你能够直接窥探任何私密的东西。

测试文件中的内容如下:

import { GdlService } from 'place';
import { NgZone } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import {
    anything,
    instance,
    mock,
    verify
} from 'ts-mockito';

describe('yada yada', () => {
    const mockNgZone = mock(NgZone);
    // Can use when(mockNgZone.whatever)... to mock what you need
    beforeEach(() => {
        TestBed.configureTestModule({
            providers: [{
                provide: NgZone,
                useValue: instance(mockNgZone)
            }]
        });
    });

    it('checks on loadApp', () => {
        verify(mockNgZone.runOutsideAngular(anything())).called();
    });
});

如果您希望仅使用spyOn方法,则只需替换提供程序的useValue部分中的对象。

答案 1 :(得分:1)

它可以像任何其他功能一样进行测试。考虑到loadApp是原型方法,它可以在类原型上存根或监视:

it('', () => {
  spyOn(<any>GdlService.prototype, 'loadApp');
  const gdl = TestBed.get(GdlService);
  expect(gdl['loadApp']).toHaveBeenCalledWith('myAppName');
});

答案 2 :(得分:1)

提高成员的可见性以进行测试是可以的。因此,为了优雅,您可能希望将loadApp公开用于模拟。然而,尝试模拟私有函数会带来一些权衡。 @estus正在回答它:

我稍微调整了一下,使用jasmine.createSpy修改原型以覆盖私有函数。

  it('try to call loadApp', () => {
    GdlService.prototype['loadApp'] = jasmine.createSpy()
      .and
      .callFake((appName) => {
      console.log('loadApp called with ' , appName );
    });
    // spyOn(IEFUserService.prototype, 'loadAppPrivate'); - this does not work because the test breaks right here trying to access private member
    const service = TestBed.get(GdlService);
    expect(service['loadApp']).toHaveBeenCalled();
  });

答案 3 :(得分:1)

在构造函数

中测试私有方法调用

Isolated unit tests被视为best practice when testing a service by the Angular Testing Guide,即不需要Angular测试实用程序。

我们无法通过监视对象实例来测试从构造函数调用的方法,因为一旦我们引用了该实例,该方法就已被调用。

相反,我们需要监视服务的原型(thanks, Dave Newton!)。在JavaScript Collection {#910 ▼ #items: array:2 [▼ "Master Classes" => Collection {#907 ▶} "Day By Day" => Collection {#908 ▶} ] } 上创建方法时,我们实际上是在class上创建方法。

鉴于这个基于MockNgZone from Angular testing internals的NgZone间谍工厂:

<ClassName>.prototype

我们可以模拟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;
}

通常我们不想测试私有方法,而是观察它所产生的公共副作用。

但是,我们可以使用Jasmine Spies来实现我们想要的目标。

See full example on StackBlitz

角度服务生命周期

See examples that demonstrate the Angular Service Lifecycle on StackBlitz。请阅读// 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); }); }); }); 文件中的注释,然后打开JavaScript控制台以查看输出的消息。

使用Jasmine测试创建Angular StackBlitz

Fork my StackBlitz用Jasmine测试Angular就像在这个答案中一样