Cannot read property 'afterClosed' of undefined when unit testing MatDialog in Jasmine

血红的双手。 提交于 2020-02-24 12:38:32

问题


I get the following error from Karma Jasmine:
TypeError: Cannot read property 'afterClosed' of undefined

I searched sincerely, but I could not find a solution in Stack Overflow or in other sources.

This is how I open the MatDialog in my component:
(Like documented)

constructor(public dialog: MatDialog) {}

public openDialog(): void {
    const dialogReference: MatDialogRef<any, any> = this.dialog.open(myDialogComponent, {
        width: '1728px'
    });

    dialogReference.afterClosed().subscribe((result) => {
    });
}

This is my unit test config:

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [
            myRequestComponent
        ],
        imports: [
            MatDialogModule,
            ReactiveFormsModule
        ],
        providers: [
            { provide: MatDialog, useValue: {} },
        ],
        schemas: [CUSTOM_ELEMENTS_SCHEMA]
    }).compileComponents();
}));

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

Here is my unit test:

it('openDialog() should open a dialog', () => {
    spyOn(component.dialog, 'open');

    component.openDialog();

    expect(component.dialog.open).toHaveBeenCalled();
});

Do I have to mock the MatDialog or the MatDialogReference?


回答1:


Lets break your issue step by step.

First, by registering

providers: [{ provide: MatDialog, useValue: {} }],

Your test bed will inject a an object with no behavior (eg. instance methods/members) whenever MatDialog needs to be resolved.

This isn't truly necessary, as you are importing the MatDialogModule into your test bed, so an instance of MatDialog can be resolved without issues. But lets stick to your approach. This solution will require you to remove that line.

Second, by doing:

spyOn(component.dialog, 'open')

you are installing a proxy for the open instance method in the object referenced by component.dialog. In this case, the empty object that you registered previously.

Despite the fact that the object does not have such a member, jasmine will dynamically add the proxy in its place. That is why you don´t see an error like this.dialog.open is not a function.

Lastly, whenever interacted with, the proxy will record information about those interactions and redirect the calls to the original open member. Because there was no original implementation, a function with no return will be used in its place, which will finally trigger the accessing foo of undefined.

TL;DR;

Remove { provide: MatDialog, useValue: {} } and use the following in order to mock the required MatDialogRef instance:

import { EMPTY} from 'rxjs';

it('openDialog() should open a dialog', () => {
    const openDialogSpy = spyOn(component.dialog, 'open')
        .and
        .returnValue({afterClosed: () => EMPTY});

    component.openDialog();

    expect(openDialogSpy).toHaveBeenCalled();
});



回答2:


You can try the below

{provide: MatDialog, useClass: MatDialogMock} // Add this in the providers array


export class MdDialogMock {
  // 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: () => Observable.of({})
    };
  }
};

OR

     let dialogSpy: jasmine.Spy;
     let dialogRefSpyObj = jasmine.createSpyObj({ afterClosed : of({}), close: 
     null });
dialogRefSpyObj.componentInstance = { body: '' };

beforeEach(() => {
    dialogSpy = spyOn(TestBed.get(MatDialog), 'open').and.returnValue(dialogRefSpyObj);
});


来源:https://stackoverflow.com/questions/54767811/cannot-read-property-afterclosed-of-undefined-when-unit-testing-matdialog-in-j

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!