Угловое модульное тестирование компонента с использованием диалога материалов для предупреждений не инициализируется - PullRequest
0 голосов
/ 10 марта 2019

Пройдя много тем в StackOverflow и на других форумах, я сдаюсь, чтобы попытаться опубликовать мою проблему в виде вопроса.

У меня есть компонент, который использует диалог материалов для отображения предупреждений, таких как всплывающие окна подтверждения или всплывающие окна информации для моего приложения. Я создал компонент с именем AlertsComponent и использовал его в своих родительских компонентах везде, где я хочу показывать предупреждения. У меня есть своя модель для обработки информации. Все это работает нормально, но spec.ts (тестовый пример) не работает даже при событии create / initialize.

My AlertsComponent.ts:

import { Component, OnInit, Optional, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { AlertInfo } from 'src/Model/common/alert-info.model';

@Component({
  selector: 'app-alerts',
  templateUrl: './alerts.component.html',
  styleUrls: ['./alerts.component.css']
})
export class AlertsComponent implements OnInit {

  constructor(
    private dialogRef: MatDialogRef<AlertsComponent>,
    @Optional() @Inject(MAT_DIALOG_DATA) public alertInfo?: AlertInfo
  ) {
    console.log('Alert Data: ' + JSON.stringify(alertInfo));
    if (alertInfo.ConfirmPopup) {
      alertInfo.Header = 'Confirm ?';
    } else { alertInfo.Header = 'Alert'; }
    this.dialogRef.disableClose = true;
  }

  ngOnInit() {
  }

  ConfirmResponse(response: boolean): void {
    this.dialogRef.close(response);
  }

  CloseAlert() {
    this.dialogRef.close();
  }

}

Мой HTML выглядит так:

<div>
  <h2 mat-dialog-title>{{alertInfo.Header}}</h2>
  <hr/>
  <mat-dialog-content>
      <strong>{{alertInfo.Body}}</strong>
      <br>
      <br>
      <!-- <strong>{{data}}</strong> -->
    </mat-dialog-content>
    <hr>
    <mat-dialog-actions>
      <div>
        <ng-container *ngIf="alertInfo.ConfirmPopup; else alertOnly">
            <button mat-button class="align-self-center" color="primary" class="button-space" (click)="ConfirmResponse(true);">YES</button>
            <button mat-button class="align-self-center" color="primary" class="button-space" (click)="ConfirmResponse(false);">NO</button>
        </ng-container>
        <ng-template #alertOnly>
            <button mat-button color="primary" class="button-space" (click)="CloseAlert();">OK</button>
        </ng-template>
      </div>
    </mat-dialog-actions>
</div>

И мои спецификации:

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

import { AlertsComponent } from './alerts.component';
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
import { AlertInfo } from 'src/Model/common/alert-info.model';
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';

describe('AlertsComponent', () => {
  let component: AlertsComponent;
  let fixture: ComponentFixture<AlertsComponent>;
  let mockDialogRef: MatDialogRef<AlertsComponent>;
  let mockAlertInfoObj: AlertInfo;
  // const MY_MAT_MOCK_TOKEN = new InjectionToken<AlertInfo>('Mock Injection Token', {
  //   providedIn: 'root',
  //   factory: () => new AlertInfo()
  // });

  @Component({
    selector: 'app-alerts',
    template: '<div><mat-dialog-content></mat-dialog-content></div>'
  })

  class MockAlertsComponent { }

  mockDialogRef = TestBed.get(MatDialogRef);
  mockAlertInfoObj = new AlertInfo();
  mockAlertInfoObj.ConfirmPopup = false;
  mockAlertInfoObj.Body = 'test alert';

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ AlertsComponent, MockAlertsComponent ],
      imports: [MatDialogModule],
      providers: [
        {provide: MatDialogRef, useValue: mockDialogRef},
        {provide: MAT_DIALOG_DATA, useValue: mockAlertInfoObj},
      ],
      schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
    })
    .compileComponents();
  }));

  TestBed.overrideModule(BrowserDynamicTestingModule, {
    set: {
      entryComponents: [AlertsComponent]
    }
  })

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

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

Когда я запускаю «ng test», этот тестовый пример компонента завершается с ошибкой:

AlertsComponent encountered a declaration exception
Error: Cannot call Promise.then from within a sync test.
Error: Cannot call Promise.then from within a sync test.
    at SyncTestZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.SyncTestZoneSpec.onScheduleTask (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:366:1)
    at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:404:1)
    at Zone../node_modules/zone.js/dist/zone.js.Zone.scheduleTask (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:238:1)
    at Zone../node_modules/zone.js/dist/zone.js.Zone.scheduleMicroTask (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:258:1)
    at scheduleResolveOrReject (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:879:1)
    at ZoneAwarePromise.then (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:1012:1)
    at ApplicationInitStatus.push../node_modules/@angular/core/fesm5/core.js.ApplicationInitStatus.runInitializers (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm5/core.js:15618:1)
    at TestBedViewEngine.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine._initIfNeeded (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm5/testing.js:1702:59)
    at TestBedViewEngine.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm5/testing.js:1766:1)
    at Function.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine.get (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm5/testing.js:1551:1)

Я не знаю или не могу понять, где я делаю что-то не так или где? Может кто-нибудь, пожалуйста, помогите мне?

Ответы [ 2 ]

0 голосов
/ 21 апреля 2019

Итак, для начала я бы использовал неглубокий подход к тестированию для тестирования этого компонента и использовал бы такую ​​настройку теста:

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

  let test: AlertInfo = {Header: 'HEADER', Body: 'BODY'};

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent, TestMatDialogActionsComponent, TestMatDialogContentComponent],
      providers: [
        {provide: MatDialogRef, useValue: {}},
        {provide: MAT_DIALOG_DATA, useValue: test}
      ],
      schemas: [NO_ERRORS_SCHEMA]
    }).compileComponents();
  }));

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

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

В этой настройке TestMatDialogActionsComponent и TestMatDialogContentComponent необходимы для насмешки над материалом диалога.

Эти компоненты тестирования могут быть либо объявлены внутри самого файла спецификации (но не экспортировать их), либо вы можете создать центральную папку тестирования рядом с вашей папкой src, куда вы помещаете эти компоненты и экспортируете их, чтобы вы могли использовать их повторно их в ваших тестах. Но убедитесь, что вы включили эту папку только в tsconfig.spec.ts, а не в tsconfig.app.ts, просто чтобы убедиться, что этот компонент случайно не используется внутри вашего приложения.

@Component({
  selector: '[mat-dialog-actions]',
  template: '<ng-content></ng-content>'
})
export class TestMatDialogActionsComponent {

  constructor() { }
}

@Component({
  selector: '[mat-dialog-content]',
  template: '<ng-content></ng-content>'
})
export class TestMatDialogContentComponent {

  constructor() { }
}

Начиная с этой настройки, вы можете добавить все, что вам нужно, чтобы проверить ваши варианты использования.

0 голосов
/ 19 апреля 2019
Stabblitz

@ erbsenkoenig помог, а затем я добавил еще несколько, чтобы решить мою потребность.ниже то, что я сделал, чтобы издеваться и MatDialog.

    export class MatDialogMock {
  // When the component calls this.dialog.open(...) we'll return an object
  // with an afterClosed method that allows to subscribe to the dialog result observable.
  public open(inputdata: any) {
    return {
      afterClosed: () => of({inputdata})
    };
  }
}

Я ссылался на это из различных других ответов Stackoverflow, чтобы быть честным.Использовал этот фиктивный класс в провайдерах.Затем мои тесты инстанцировали его как

// arrange
const mockAddEditDialogObj = MatDialogMock.prototype;
let dialogRef = jasmine.createSpyObj(mockAddEditDialogObj.open.name, ['afterClosed']);
dialogRef.afterClosed.and.returnValue(of(true));

// act
component.AddNew();
dialogRef = mockAddEditDialogObj.open(EditProjectComponent.prototype);
const result = dialogRef.afterClosed();

// assert
expect(dialogRef).toBeTruthy();

. Вы можете расширить тесты и макетировать объекты, возвращаемые классом, как необходимо для ваших тестов.

...