javascript MatDialog 服务单元测试 Angular 6 错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/52993583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:05:08  来源:igfitidea点击:

MatDialog Service Unit Test Angular 6 Error

javascriptangularunit-testingjasminekarma-jasmine

提问by rj.learn

I'm having modal service to open, confirm and close dialog and i am making its unit test file but i got and error on Angular and this is the code.

我有模式服务来打开、确认和关闭对话框,我正在制作它的单元测试文件,但我在 Angular 上遇到错误,这是代码。

modal.service.ts

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

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

  });

});

So with that code the error is

所以使用该代码的错误是

TypeError: Cannot read property 'componentInstance' of undefined

Can you help me how to make this successful? Help is much appreciated.

你能帮我如何使这个成功吗?非常感谢帮助。

回答by dmcgrandle

Testing mat-dialogs can be tricky. I tend to use a spy object for the return from a dialog open (dialogRefSpyObj below) so I can more easily track and control tests. In your case it might look something like the following:

测试垫子对话框可能很棘手。我倾向于使用间谍对象从打开的对话框(下面的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();
    });
});

回答by George C.

I have a better solution that still works on 2019

我有一个更好的解决方案,它仍然适用于 2019

header.component.ts

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

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


}

回答by Maarti

I do not have the exact answer for your case but I also did some tests on MatDialog. I can show you what I did. Maybe look at the inject()part:

对于您的情况,我没有确切的答案,但我也对MatDialog. 我可以告诉你我做了什么。也许看一下inject()部分:

(I deleted some things for clarity and confidentiality)

(为了清晰和保密,我删除了一些东西)

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

});