метод внутри сервиса не работает в angular 5 модульных тестах с кармой - PullRequest
0 голосов
/ 05 марта 2020

Я пытаюсь написать тесты кармы для метода внутри службы, но непосредственное получение имени метода службы не является ошибкой функции в тестовых случаях.

Кто-нибудь сталкивался с такой же проблемой?

ниже приведен мой код.

Я пытался шпионить за методом и возвращать нулевую наблюдаемую величину, пока метод не определен в тестах кармы.

import { DialogModal } from './dialog.model';
import { Component, OnInit } from '@angular/core';
import { DialogService } from './dialog.service';

declare var $: any;

@Component({
  selector: 'app-dialog',
  templateUrl: './dialog.component.html',
  styleUrls: ['./dialog.component.css']
})
export class DialogComponent implements OnInit {
  displayModal: boolean;
  dialogDetails: DialogModal;
  constructor(public dialogService: DialogService) {
    this.subscribeToDialogService();
  }

  ngOnInit(): void {}
  /**
   * This function will subscribe for the changes sent by the openDialogModal
   * dialogSubject.
   */
  subscribeToDialogService(): void {
    this.dialogService.getDialogDetails().subscribe(data => {
      if (data) {
        this.dialogDetails = new DialogModal();
        this.dialogDetails = data;
        this.displayModal = true;
        setTimeout(() => {
          if (this.dialogDetails && this.dialogDetails.isAlertBox) {
            $('#dialogCompokButton').focus();
          } else {
            $('#dialogComprejectButton').focus();
          }
        }, 300);
      }
    });
  }
  /**
   * This function will be called when the user clicks on the positive response
   * in the dialog that appears and in turn will call the dialogConfirmation()
   * function to return the promise as true back to the calling component
   */
  confirmDialog(): void {
    this.displayModal = false;
    this.dialogService.dialogConfirmation();
  }
  /**
   * This function will be called when the user clicks on the negative response
   * in the dialog that appears and in turn will call the dialogRejection()
   * function to return the promise as false back to the calling component
   */
  rejectDialog(): void {
    this.displayModal = false;
    this.dialogService.dialogRejection();
  }
}

Служба:

import { DialogModal } from './dialog.model';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class DialogService {
  dialogConfirmation: () => void;
  dialogRejection: () => void;
  dialogSubject: BehaviorSubject<DialogModal> = new BehaviorSubject(undefined);
  constructor() {}
  /**
   * This function is called whenever the user need to display a dialog.
   * The user can set the icon, buttons, button labels, dialog message and heading
   * The function will return a promise based on the button clicked.
   */
  openDialogModal(dialogInputs: DialogModal): Promise<boolean> {
    if (dialogInputs)
      this.dialogSubject.next({...dialogInputs});

    return new Promise<boolean>((resolve, reject) => {
      this.dialogConfirmation = () => resolve(true);
      this.dialogRejection = () => resolve(false);
    });
  }
  /**
   * This function will be called in the dialog component to observe the changes made to
   * the behaviour subject and in order to get the latest values of the details to be shown
   * in the dialog.
   */
  getDialogDetails(): Observable<DialogModal> {
    return this.dialogSubject.asObservable();
  }
}

Контрольные примеры:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { DialogComponent } from './dialog.component';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { DialogService } from './dialog.service';
import { of } from 'rxjs/observable/of';

describe('DialogComponent', () => {
  let component: DialogComponent;
  let dialogservice: DialogService;
  let fixture: ComponentFixture<DialogComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ DialogComponent ],
      providers: [DialogService],
      schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(DialogComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should create', () => {
    component.confirmDialog();
    component.dialogService.dialogConfirmation();
    spyOn(dialogservice, 'dialogConfirmation').and.callFake(() => {
      return of(null);
  });
    expect(component.displayModal).toBeFalsy();
    expect(component.dialogService.dialogConfirmation).toHaveBeenCalled();
  });
});

Ошибка:

enter image description here

1 Ответ

1 голос
/ 05 марта 2020

Лично я не стал бы писать модульный тест для компонента, который затем проверял бы службу, используемую в этом компоненте.
Я написал бы непосредственно модульный тест для службы. А в компоненте я бы издевался над сервисом. При таком подходе мои модульные тесты выполняются намного быстрее. И они не сломаются из-за ошибки в сервисе. Только тесты сервисной единицы будут / должны сломаться. Это значительно упрощает анализ ошибок.

Но о вашей проблеме:
В своем коде вы пишете:

component.dialogService.dialogConfirmation();
spyOn(dialogservice, 'dialogConfirmation').and.callFake(() => {
  return of(null);
});

Это содержит два fl aws.
Первый недостаток заключается в том, что метод вызывается FIRST, и только позже он изменяется, чтобы вызвать ложную реализацию. Сначала вам нужен шпион и перенаправить вызов метода, затем вы можете вызвать метод.
Второй недостаток заключается в том, что spyOn ожидает в качестве первого параметра объект, а затем в качестве второго параметра имя метода. В вашем примере кода он не получает объект (объект будет component.dialogService).

Так что это должно быть

spyOn(component.dialogService, 'dialogConfirmation').and.callFake(() => {
  return of(null);
});
component.dialogService.dialogConfirmation();

Кстати, вы можете сократить шпиона до

spyOn(component.dialogService, 'dialogConfirmation').and.return(of(null))

Возможно, еще одна вещь.
Когда вы подписываетесь на Observables, которые НЕ завершатся самостоятельно (вызовы http завершаются после первого выброса), то вы должны убедиться, что ваш компонент отписался, прежде чем он будет уничтожен. , Причина в том, что с «подпиской» вы регистрируете метод в Observable. Это означает, что, даже если ваш компонент долго отбрасывался go, этот метод все еще будет активен и будет выполняться с каждым излучением. Простой способ отменить подписку - добавить все подписки в объект подписки.

private subscriptions: Subscription = new Subscription();
// ...
this.subscriptions.add(
   this.myService.myMethod.subscribe( ... )
);
this.subscriptions.add(
   this.myOtherService.myOtherMethod.subscribe( ... )
)

ngOnDestroy(){
  this.subscriptions.unsubscribe();
}
...