浏览了StackOverflow和其他论坛上的许多主题之后,我放弃尝试并愿意将我的问题发布为问题。
我有一个组件,该组件使用“材质对话框”显示我的应用程序的警报,例如“确认”弹出窗口或“信息”弹出窗口。我创建了一个名为AlertsComponent的组件,并在我想显示警报的任何地方的父组件中使用它。我有自己的模型来处理信息。所有这些都工作正常,但是即使在create / initialize事件上,spec.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();
}
}
我的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)
我不知道或无法弄清楚,我在哪里做错什么,或者在哪里?有人可以帮我吗?
答案 0 :(得分:0)
@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();
您可以根据需要扩展测试和模拟类返回对象。
答案 1 :(得分:0)
因此,首先,我将使用浅层测试方法来测试该组件,并使用如下测试设置:
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
来模拟材质对话框的内容。
这些测试组件可以在spec文件本身中声明(但不导出),也可以在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() { }
}
从该设置开始,您可以添加所需的所有内容,以测试用例。