Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Angular Material Dialog afterClosed() for unit test

I am opening my mat-dialog with the following function:

accept() {
  let dialogRef = this.dialog.open(AcceptDialogComponent, {
    data: {
      hasAccepted: false
    }
  })
  dialogRef.afterClosed().subscribe(result => {
    console.log(result);
    if (result.hasAccepted === true) {
      this.leadService.acceptLead(this.holdingAccountId, this.lead.id)
        .pipe(
          takeUntil(this.onDestroy$)
        )
        .subscribe(acceptLeadRes => {
            console.log(acceptLeadRes);
            this.leadService.updateLeadAction('accept');
          },
          (err: HttpErrorResponse) => {
            console.log(err);
            this.router.navigate(['/error']);
          });
    }
  });
}

I am attempting to write a test for this function that simply fires the afterClosed() so that I can check if my service method that makes a backend call is called.

component.spec.ts (beforeEach Testbed creation)

beforeEach(async (() => {
  TestBed.configureTestingModule({
      declarations: [LeadCardComponent, AcceptDialogComponent],
      imports: [
        requiredTestModules,
        JwtModule.forRoot({
          config: {
            tokenGetter: () => {
              return '';
            }
          }
        })
      ],
      providers: [
        ApplicationInsightsService,
        JwtHelperService,
        // { provide: LeadsService, useValue: leadServiceSpy }
      ],
    }),

    TestBed.overrideModule(BrowserDynamicTestingModule, {
      set: {
        entryComponents: [AcceptDialogComponent]
      }
    });
  TestBed.compileComponents();
}));

component.spec.ts (test)

it('Return from AcceptLeadDialog with hasAccepted equals true should call acceptLead endpoint', () => {
  let matDiaglogref = dialog.open(AcceptDialogComponent, {
    data: {
      hasAccepted: false
    }
  });
  spyOn(matDiaglogref, 'afterClosed').and.callThrough().and.returnValue({
    hasAccepted: true
  });
  spyOn(leadService, 'acceptLead').and.callThrough();
  component.acceptLead();
  fixture.detectChanges();
  matDiaglogref.close();
  fixture.detectChanges();

  expect(leadService.acceptLead).toHaveBeenCalled();
});

The test currently fails with a "Expected spy acceptLead to have been called." I am failing on understanding how to test the function and execute some sort of mock MatDialogRef so that I can check if the conditions of my test pass.

Any help/suggestions would be much appreciated

Update Working Test implemented from Accepted Answer

it('Return from AcceptLeadDialog with hasAccepted equals true should call acceptLead endpoint', () => {
  spyOn(component.dialog, 'open')
    .and
    .returnValue({
      afterClosed: () => of({
        hasAccepted: true
      })
    });
  spyOn(leadService, 'acceptLead').and.callThrough();
  component.acceptLead();
  expect(component.dialog).toBeDefined();
  expect(leadService.acceptLead).toHaveBeenCalled();
});
like image 734
Brian Stanley Avatar asked May 07 '19 20:05

Brian Stanley


People also ask

How to test Angular Material dialog's afterclosed () method?

You can test Angular Material Dialog's afterClosed method this way: 1 import { of } from 'rxjs'; 2 spy on the dialog and return an observable for the afterClosed () method More ...

When do I need to mock a component in unit testing?

Of course, if you have a test that needs to interact with another component, such as through a view child, you need to have a mock of the component, if you don’t want to create an integration test. What you will often see in Angular libraries, eg. Angular Routing library, is that it provides a testing module for easier mocking in unit tests.

How do I create mocks for angular components?

What you will often see in Angular libraries, eg. Angular Routing library, is that it provides a testing module for easier mocking in unit tests. I recommend that you do the same with components you want to create mocks for, by creating a *component-name*.component.mock.ts beside the component file, so you can easily get a mock of the component.

What is the use of angular routing in unit testing?

Angular Routing library, is that it provides a testing module for easier mocking in unit tests. I recommend that you do the same with components you want to create mocks for, by creating a *component-name*.component.mock.ts beside the component file, so you can easily get a mock of the component.


2 Answers

I solved this problem as the first post by @Adithya Sreyaj but added the next change:

spyOn(component.dialog, 'open')
    .and
    .returnValue({
        afterClosed: () => of(true)
    } as MatDialogRef<typeof component>);
like image 173
CrgioPeca88 Avatar answered Oct 18 '22 05:10

CrgioPeca88


You can test Angular Material Dialog's afterClosed method this way:

  1. import { of } from 'rxjs';
  2. spy on the dialog and return an observable for the afterClosed() method
spyOn(component.dialog, 'open')
     .and
     .returnValue({afterClosed: () => of(true)});

Basically, the afterClosed() of the dialogRef is expecting an Observable. So we spy on the component's dialog open method and return an Observable for the afterClosed() method by using the of operator from rxjs.

You can then replace the of(true) from the returnValue with your own data what you are sending in the afterClosed() of the dialog in the main component.

like image 31
Adithya Sreyaj Avatar answered Oct 18 '22 05:10

Adithya Sreyaj