我目前正在为使用MatDialog打开模式的服务构建单元测试。每种模态都在其各自的组件文件中定义。我设法为组件创建了单元测试,没有出现问题,但是服务测试抛出了以下错误:
× should successfully create an alert popup with default config
Chrome 67.0.3396 (Windows 10 0.0.0)
Failed: this.dialog.open is not a function
at <Jasmine>
at PopupService.alert (dist/dev/app/shared/ui/popup/popup.service.js:9:7503)
at UserContext.eval (dist/dev/app/shared/ui/popup/popup.service.spec.js:19:26)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:388:26)
at AsyncTestZoneSpec.onInvoke (node_modules/zone.js/dist/async-test.js:106:39)
at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:125:39)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:387:32)
at Zone.runGuarded (node_modules/zone.js/dist/zone.js:151:47)
at runInTestZone (node_modules/zone.js/dist/async-test.js:234:29)
at UserContext.<anonymous> (node_modules/zone.js/dist/async-test.js:168:17)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:388:26)
at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:128:39)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:387:32)
at Zone.run (node_modules/zone.js/dist/zone.js:138:43)
at runInTestZone (node_modules/zone.js/dist/jasmine-patch.js:142:34)
at UserContext.<anonymous> (node_modules/zone.js/dist/jasmine-patch.js:158:20)
at <Jasmine>
at ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:421:31)
at Zone.runTask (node_modules/zone.js/dist/zone.js:188:47)
at drainMicroTaskQueue (node_modules/zone.js/dist/zone.js:595:35)
正在运行测试本身,但是(在服务中)调用MatDialog.open()时会引发此错误。当应用程序在开发中运行时,不会引发此错误。
以下是与该问题相关的代码段:
服务的测试用例:
import { inject, TestBed, async } from "@angular/core/testing";
import { PopupService } from "./popup.service";
import { MatDialogModule, MatDialog } from "@angular/material";
import { Injectable } from "@angular/core";
import { PopupAlertComponent } from './popup.component';
export function main() {
describe('Service: PopupService', () => {
let dialog: MatDialog,
popupService: PopupService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: MatDialog, useValue: {} } //This is a workaround: otherwise a NullInjectorError is thrown
]
});
dialog = TestBed.get(MatDialog);
popupService = new PopupService(dialog);
});
/*********************
* CONFIRM *
********************/
it('should successfully create an confirm popup with default config',
async(() => {
popupService.confirm("test", "this is a test", "Ok", "Cancel");
}));
it('should successfully create an confirm popup with custom config',
async(() => {
let config = { disableClose: false, width: '850px' };
popupService.confirm("test", "this is a test", "Ok", "Cancel", config);
}));
});
}
模态服务:
import { Injectable } from "@angular/core";
import { MatDialog, MatDialogRef } from "@angular/material";
import {
PopupAlertComponent, PopupConfirmComponent, PopupInputComponent,
PopupBillerListComponent, PopupListComponent
} from "./popup.component";
export class KeyValuePair {
id: number;
name: string;
}
export class ListObject {
id: number;
name: string;
imageUrl: string;
}
/**
* Class used to generate popups through the app, by calling one of the functions. (alert, input, confirm..)
*/
@Injectable()
export class PopupService {
/*
Base config for popups, passed in through mdDialog config options.
Can be temporarily modified on a specific popup, if necessary.
*/
config = {
disableClose: true,
width: '330px'
};
constructor(public dialog: MatDialog) {
}
/**
* Base popup function that sets the popup text to specified strings, or allow the default values specified in the popup.component.
* @param popupRef
* @param {boolean} hasButtons
* @param {string} title
* @param {string} message
* @param {string} okText
* @param {string} cancelText
* @returns {Observable<any>}
*/
popup(popupRef: any, hasButtons: boolean, title: string, message: string, okText: string, cancelText: string) {
if (title !== "")
popupRef.componentInstance.title = title;
if (message !== "")
popupRef.componentInstance.message = message;
if (hasButtons) {
if (okText !== "")
popupRef.componentInstance.okText = okText;
if (cancelText !== "")
popupRef.componentInstance.cancelText = cancelText;
}
return popupRef.afterClosed();
}
/**
* Input popup function with input popup specific features
* @param {string} title
* @param {string} message
* @param {string} okText
* @param {string} cancelText
* @param {object} config (optional)
* @returns {Observable<any>}
*/
input(title: string, message: string, okText: string, cancelText: string, config?: {}) {
let popupRef: MatDialogRef<PopupInputComponent>;
popupRef = this.dialog.open(PopupInputComponent, config ? config : this.config);
return this.popup(popupRef, true, title, message, okText, cancelText);
}
}
模式部分:
import { Component } from "@angular/core";
import { MatDialogRef } from "@angular/material";
import { PopupService, KeyValuePair } from "./popup.service";
/**
* Input Popup
* Popup that will be used in cases where a single input popup is required
*/
@Component({
moduleId : module.id,
selector: 'tp-input-popup',
template: `
<div class="body">
<mat-dialog-content>
<h4><p class="text-center">{{title | translate}}</p></h4>
<hr>
</mat-dialog-content>
<mat-dialog-actions align="end">
<div class="group">
<input #input
id="input"
type="tel"
[ngClass]="input.validity.valid ? 'used' : false"
required>
<span class="highlight"></span>
<span class="bar"></span>
<label>{{message | translate}}</label>
</div>
<div class="button-position">
<button mat-button type="button" class="button" (click)="popup.close(false)">
<span><b>{{cancelText | translate}}</b></span>
</button>
<button mat-raised-button type="button" class="button" (click)="popup.close(input.value)">
<span><b>{{okText | translate}}</b></span>
</button>
</div>
</mat-dialog-actions>
</div>
`,
styleUrls : ['popup.component.css']
})
export class PopupInputComponent {
title: string = "Input";
message: string = "Enter your data here:";
okText: string = "Ok";
cancelText: string = "Cancel";
constructor(public popup: MatDialogRef<PopupInputComponent>) {
}
}
有多个函数(例如输入),每个函数都调用不同的模式,但是为了简洁起见,我仅包含其中一个。如果需要,我可以在问题的代码片段中包含其他模式类型。
我在搜索过程中发现了其他问题,这些问题看起来很相似,但是没有一个问题可以解决Karma中的这一特定错误。