MatDialog 服务单元测试 Angular 6 错误
Posted
技术标签:
【中文标题】MatDialog 服务单元测试 Angular 6 错误【英文标题】:MatDialog Service Unit Test Angular 6 Error 【发布时间】:2019-03-30 07:41:21 【问题描述】:我有模式服务来打开、确认和关闭对话框,我正在制作它的单元测试文件,但我在 Angular 上遇到错误,这是代码。
modal.service.ts
@Injectable()
export class ModalService
constructor(private dialog: MatDialog)
public open<modalType>(modalComponent: ComponentType<modalType>): Observable<any>
let dialogRef: MatDialogRef<any>;
dialogRef = this.dialog.open(modalComponent,
maxWidth: '100vw'
);
console.log(dialogRef)
dialogRef.componentInstance.body = body;
return dialogRef.afterClosed().pipe(map(result => console.log('test'); );
modal.service.spec.ts
export class TestComponent
describe('ModalService', () =>
let modalService: ModalService;
const mockDialogRef =
open: jasmine.createSpy('open')
;
beforeEach(async(() =>
TestBed.configureTestingModule(
imports: [ MatDialogModule ],
providers: [
ModalService,
MatDialogRef,
provide: MatDialog, useClass: MatDialogStub
]
).compileComponents();
modalService = TestBed.get(ModalService);
));
it('open modal', () =>
modalService.open(DummyComponent, '300px');
expect(modalService.open).toHaveBeenCalled();
);
);
所以使用该代码的错误是
TypeError: Cannot read property 'componentInstance' of undefined
你能帮助我如何使这个成功吗?非常感谢您的帮助。
【问题讨论】:
检查这个mat对话框的例子,确保所有需要的模块都已经导入stackblitz.com/angular/gxyboyyobmo @DanielC。嘿,谢谢您的建议,但我正在寻找单元测试答案。该服务运行良好,在组件中被调用,但在单元测试中没有 【参考方案1】:测试mat-dialogs
可能很棘手。我倾向于使用间谍对象从打开的对话框(下面的dialogRefSpyObj
)返回,这样我可以更轻松地跟踪和控制测试。在您的情况下,它可能类似于以下内容:
describe('ModalService', () =>
let modalService: ModalService;
let dialogSpy: jasmine.Spy;
let dialogRefSpyObj = jasmine.createSpyObj( afterClosed : of(), close: null );
dialogRefSpyObj.componentInstance = body: '' ; // attach componentInstance to the spy object...
beforeEach(() =>
TestBed.configureTestingModule(
imports: [MatDialogModule],
providers: [ModalService]
);
modalService = TestBed.get(ModalService);
);
beforeEach(() =>
dialogSpy = spyOn(TestBed.get(MatDialog), 'open').and.returnValue(dialogRefSpyObj);
);
it('open modal ', () =>
modalService.open(TestComponent, '300px');
expect(dialogSpy).toHaveBeenCalled();
// You can also do things with this like:
expect(dialogSpy).toHaveBeenCalledWith(TestComponent, maxWidth: '100vw' );
// and ...
expect(dialogRefSpyObj.afterClosed).toHaveBeenCalled();
);
);
【讨论】:
@dmcgrandie 我同意 matdialog 测试很棘手,感谢您的解决方案。它对我有用。谢谢你 如何触发dialogRefSpyObj.afterClosed
?
@PinguinoSod - 如何触发它取决于您的代码。请随时就您的详细信息提出另一个问题。请务必包含代码示例,展示您迄今为止所做的尝试。
您将如何测试由另一个模态打开的模态? (应用程序>单击按钮>第一个基本确认模式>单击是>在新模式中打开组件)
我会独立测试这两个模态(每个都有自己的一组测试)。【参考方案2】:
我有一个更好的解决方案,在 2019 年仍然有效
header.component.ts
import BeforeLogOutComponent from '@app-global/components/before-log-out/before-log-out.component';
/**
* The method triggers before the logout.
* Opens the dialog and warns the user before log Out.
*/
public beforeLogOut(): void
this._dialog.open(BeforeLogOutComponent, width: '400px', disableClose: false, panelClass: 'dialog_before_log_out' )
.afterClosed()
.subscribe((res) =>
if (res && res.action === true) this.loggedOut();
, err =>
console.error(err);
);
header.component.spec.ts
import async, ComponentFixture, TestBed from '@angular/core/testing';
import MatDialog from '@angular/material';
import Observable, of from 'rxjs';
<<-- Create a MatDialog mock class -->>
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.
open()
return
afterClosed: () => of(action: true)
;
describe('HeaderComponent', () =>
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async(() =>
TestBed.configureTestingModule(
imports: [
MaterialModule, RouterTestingModule, HttpModule, BrowserAnimationsModule,
HttpClientModule, FlexLayoutModule,
],
declarations: [
HeaderComponent,
],
providers: [
provide: MatDialog, useClass: MatDialogMock <<-- look this
]
)
.compileComponents();
));
beforeEach(async() =>
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
component.ngOnInit();
component.ngAfterViewInit();
await fixture.whenStable();
fixture.detectChanges();
);
fit('should create', () =>
expect(component).toBeTruthy();
);
// I test the dialog here.
fit('should open the dialog', () =>
component.beforeLogOut();
);
【讨论】:
实际上,我上面其他答案中的间谍对象解决方案在 2019 年的 Angular 7 中工作得很好。:) 我更喜欢你提供的模拟类方法,尽管它们都可以解决问题,但它是只是选择哪一个的偏好问题。 hmm,不确定这将如何测试afterClosed
,因为它实际上从未被调用过,因为在规范文件中没有告诉对话框关闭。
请考虑解释您的代码如何帮助解决 OP 的问题,而不是简单地灌输代码。
谢谢,它在我的情况下工作,模拟整个对话响应,解决了问题并根据需要返回真/假。【参考方案3】:
对于您的情况,我没有确切的答案,但我也对MatDialog
进行了一些测试。我可以告诉你我做了什么。也许看看inject()
部分:
(为了清晰和保密,我删除了一些内容)
describe('MyDialogComponent', () =>
let dialog: MatDialog;
let overlayContainer: OverlayContainer;
let component: MyDialogComponent;
let fixture: ComponentFixture<MyDialogComponent>;
const mockDialogRef =
close: jasmine.createSpy('close')
;
beforeEach(async(() =>
TestBed.configureTestingModule(
imports: [
BrowserAnimationsModule,
ReactiveFormsModule,
AngularMaterialModule,
],
providers: [
provide: MatDialogRef, useValue: mockDialogRef ,
provide: MAT_DIALOG_DATA,
useValue:
title: 'myTitle',
],
declarations: [MyDialogComponent],
);
TestBed.overrideModule(BrowserDynamicTestingModule,
set:
entryComponents: [MyDialogComponent]
);
TestBed.compileComponents();
));
beforeEach(inject([MatDialog, OverlayContainer],
(d: MatDialog, oc: OverlayContainer) =>
dialog = d;
overlayContainer = oc;
)
);
afterEach(() =>
overlayContainer.ngOnDestroy();
);
beforeEach(() =>
fixture = TestBed.createComponent(MyDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
);
it('should create', () =>
expect(component).toBeTruthy();
);
it('onCancel should close the dialog', () =>
component.onCancel();
expect(mockDialogRef.close).toHaveBeenCalled();
);
);
【讨论】:
【参考方案4】:将此部分添加到提供程序部分
provide: MAT_DIALOG_DATA, useValue: ,
provide: MatDialogRef, useValue: ,
查看下方
describe('MyDialogComponent', () =>
let component: MyDialogComponent;
let fixture: ComponentFixture<MyDialogComponent>;
beforeEach(async(() =>
TestBed.configureTestingModule(
imports: [
MatDialogModule,
],
declarations: [MyDialogComponent],
providers: [
provide: MAT_DIALOG_DATA, useValue: ,
provide: MatDialogRef, useValue: ,
],
).compileComponents();
));
);
【讨论】:
【参考方案5】:此答案不直接回答问题,但适用于像我这样的人,因为您在打开对话框后无法查询对话框。
使用spectator,您只需将 root: true
作为查询的选项。
找不到对话框,因为它不是您正在测试的组件的子组件,但使用 root: true
将搜索整个页面。
例如:spectator.query(byTestId('myTestId'), root: true )
【讨论】:
以上是关于MatDialog 服务单元测试 Angular 6 错误的主要内容,如果未能解决你的问题,请参考以下文章
Angular - 模块 AppModule 导入的意外值 MatDialog
在我的函数 ngDoCheck() Angular 中关闭 matDialog 时出现问题
Angular - 模块AppModule导入的意外值MatDialog
Angular Material MatDialog 未正确销毁