简体   繁体   English

使用“材质对话框”发出警报的组件上的角单元测试未初始化

[英]Angular Unit testing on a component using Material Dialog for alerts is not getting initialized

After going through many topics in StackOverflow and other forums, i am giving up to try and willing to post my issue as a question. 在经历了StackOverflow和其他论坛的许多主题之后,我放弃尝试并愿意将我的问题作为问题发表。

I have a component that uses Material Dialog to show alerts like Confirmation popups or Information popups for my app. 我有一个使用“材料对话框”来显示警报的组件,例如针对我的应用的“确认”弹出窗口或“信息”弹出窗口。 I created a component called AlertsComponent and using that in my parent components wherever i want to show the alerts. 我创建了一个名为AlertsComponent的组件,并在我想显示警报的任何地方的父组件中使用它。 I have my own model to handle the information. 我有自己的模型来处理信息。 All of it is working fine but the spec.ts (test case) is failing even on the create/initialize event. 所有这些都工作正常,但是即使在create / initialize事件上,spec.ts(测试用例)也失败了。

My AlertsComponent.ts: 我的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(); } } 

My HTML looks like: 我的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> 

And my spec.ts is: 我的规格是:

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();
  });
});

When i run "ng test", this component test case fails with error saying: 当我运行“ 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)

I dont know or could figure out, where i am doing something wrong, or where? 我不知道或无法弄清楚,我在哪里做错了,或者在哪里? Can someone please help me? 有人可以帮帮我吗?

@erbsenkoenig's stackblitz helped, and then i added few more to solve my need. @erbsenkoenig的stackblitz有所帮助,然后我又添加了一些东西来解决我的需求。 below is what i did to mock and the MatDialog. 下面是我做的模仿和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})
    };
  }
}

I referenced this from various other Stackoverflow answers to be honest. 老实说,我从其他Stackoverflow答案中引用了这一点。 Used this mock class in the providers. 在提供程序中使用了此模拟类。 Then had my tests instantiated it as 然后让我的测试实例化为

// 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();

You can expand the tests and mock class return objects as needed for your tests. 您可以根据需要扩展测试和模拟类返回对象。

So to start with, I would use the shallow testing approach to test that component and would use a test setup like this: 因此,首先,我将使用浅层测试方法来测试该组件,并使用如下测试设置:

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();
  });
});

In this setup the TestMatDialogActionsComponent and TestMatDialogContentComponent are needed to mock the material dialog stuff. 在此设置中,需要TestMatDialogActionsComponentTestMatDialogContentComponent来模拟材质对话框的内容。

Those testing components could either be declared inside the spec file itself (but don't export them) or you could create a central test folder next to your src folder, where you put those components and export them, so you can reuse them in your tests. 这些测试组件可以在spec文件本身中声明(但不导出),也可以在src文件夹旁边创建一个中央测试文件夹,在其中放置这些组件并进行导出,以便可以在您的表中重复使用它们。试验。 But make sure to only include this folder inside your tsconfig.spec.ts and not inside your tsconfig.app.ts just to make sure this component is not accidentally used inside your app. 但请务必只包括里面的这个文件夹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() { }
}

Starting with that setup you can add everything you need, to test your use cases. 从该设置开始,您可以添加所需的所有内容,以测试用例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM