19
votes

I am new to angular development and more new towards unit testing using jasmine. I have created a component to sow a dialog using angular material MatDialogRef, MAT_DIALOG_DATA from @angular/material. The component is working fine but the Unit testing is giving me an error which i am not able to resolve.

I really need this to work and any help would be appreciated.... Thanks in advance..!!!

Please find my code below:

app.module.ts

        import { BrowserModule } from '@angular/platform-browser';
    import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
    import { HttpClientModule, HttpClient } from '@angular/common/http';
    import { HttpModule } from '@angular/http';
    import { RouterModule, Routes } from '@angular/router';
    import 'hammerjs';
    import { NgxPhoneSelectModule } from 'ngx-phone-select';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';
    import { MatInputModule, MatButtonModule, MatSelectModule } from '@angular/material';
    import { MatGridListModule } from '@angular/material';
    import { MatTableModule } from '@angular/material';
    import { MatDialogModule} from '@angular/material';
    import { MatCardModule} from '@angular/material';
    import { MaterialModule } from './modules/material/material.module';


    import { AppComponent } from './app.component';
    import { CustomerComponent } from './components/customer/customer.component';
    import { LoginComponent } from './components/login/login.component';
    import { ForgetPasswordComponent } from './components/forget-password/forget-password.component';
    import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
    import { DashboardComponent } from './components/dashboard/dashboard.component';

    import { LoaderService } from './services/loader.service';
    import { CustomerDataService } from './services/customer-data.service';
    import { UserService } from './services/user/user.service';


    import { HeaderComponent } from './components/header/header.component';
    import { UpdateCustomerComponent } from './components/update-customer/update-customer.component';
    import { AuthService } from './services/auth.service';
    import { AuthGuard } from './services/auth/auth.guard';
    import { DeleteCustomerComponent } from './components/delete-customer/delete-customer.component';
    import { FooterComponent } from './components/footer/footer.component';
    const appRoutes: Routes = [
      {
        path: '',
        component: LoginComponent
      },
      {
        path: 'create-customer',
        component: CustomerComponent,
        //        canActivate: [AuthGuard] // ristrict direct access of links
      },
      {
        path: 'forget-password',
        component: ForgetPasswordComponent,
        //        canActivate: [AuthGuard] // ristrict direct access of links
      },
      {
        path: 'dashboard',
        component: DashboardComponent,
        //        canActivate: [AuthGuard] // ristrict direct access of links
      },
      {
        path: 'update-customer',
        component: UpdateCustomerComponent,
        //        canActivate: [AuthGuard] // ristrict direct access of links
      },
      {
        path: '**',
        component: PageNotFoundComponent
      }
    ];

    @NgModule({
      declarations: [
        AppComponent,
        CustomerComponent,
        LoginComponent,
        ForgetPasswordComponent,
        PageNotFoundComponent,
        DashboardComponent,
        HeaderComponent,
        UpdateCustomerComponent,
        DeleteCustomerComponent,
        FooterComponent
      ],
      imports: [
        BrowserModule,
        HttpClientModule,
        HttpModule,
        RouterModule.forRoot(appRoutes),
        NgxPhoneSelectModule,
        BrowserAnimationsModule,
        FormsModule,
        ReactiveFormsModule,
        MatInputModule,
        MatButtonModule,
        MatSelectModule,
        MatGridListModule,
        MatTableModule,
        MaterialModule
      ],
      entryComponents: [
          DeleteCustomerComponent
      ],
      schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
    providers: [LoaderService, AuthService, AuthGuard, UserService, CustomerDataService],

      bootstrap: [AppComponent]
    })
    export class AppModule { }

delete customer component

        import { Component, OnInit, Inject } from '@angular/core';
    import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';

    import { Router} from '@angular/router';
    @Component({
      selector: 'app-delete-customer',
      templateUrl: './delete-customer.component.html',
      styleUrls: ['./delete-customer.component.scss']
    })
    export class DeleteCustomerComponent implements OnInit {

      constructor(private router: Router, public deleteCustDialogRef: MatDialogRef<DeleteCustomerComponent>, @Inject(MAT_DIALOG_DATA) public data: string) { }

      ngOnInit() {
      }

    onClosedeleteCustomer() {
        this.deleteCustDialogRef.close('confirm');
        this.router.navigate(['./dashboard']);
      }
      onCloseCancel() {
        this.deleteCustDialogRef.close('cancel');

    }
    }

delete-customer.component.spec.ts

        import { async, ComponentFixture, TestBed } from '@angular/core/testing';
    import {RouterTestingModule} from '@angular/router/testing';
    import { DeleteCustomerComponent } from './delete-customer.component';
    import { MaterialModule } from '../../modules/material/material.module';
    import { MatDialogRef, MAT_DIALOG_DATA, MatDialog, MatDialogModule  } from '@angular/material';
    describe('DeleteCustomerComponent', () => {
      let component: DeleteCustomerComponent;
      let fixture: ComponentFixture<DeleteCustomerComponent>;

      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [ DeleteCustomerComponent ],
          imports: [ MaterialModule, RouterTestingModule, MatDialogModule ],
          providers : [ MatDialogRef, MAT_DIALOG_DATA, MatDialog ]
        })
        .compileComponents();
      }));

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

      it('should create', () => {
        expect(component).toBeTruthy();
      });
    });

karma error

        DeleteCustomerComponent should create
        Failed: Can't resolve all parameters for MatDialogRef: (?, ?, ?).
        Error: Can't resolve all parameters for MatDialogRef: (?, ?, ?).
        at syntaxError (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:485:22)
        at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver._getDependenciesMetadata (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15662:1)
        at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver._getTypeMetadata (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15497:1)
        at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver._getInjectableMetadata (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15477:1)
        at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver.getProviderMetadata (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15837:1)
        at http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15748:1
        at Array.forEach (<anonymous>)
        at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver._getProvidersMetadata (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15708:1)
        at CompileMetadataResolver.webpackJsonp.../../../compiler/esm5/compiler.js.CompileMetadataResolver.getNgModuleMetadata (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler.js:15276:1)
        at JitCompiler.webpackJsonp.../../../compiler/esm5/compiler.js.JitCompiler._loadModules (http://localhost:9876/_karma_webpack_/webpack:/C:/dewatering_FST/node_modules/@angular/compiler/esm5/compiler
3
I think you're missing entryComponents. Also, "ristrict" should be spelt as "restrict".Edric

3 Answers

27
votes

Depending on your needs, a more simple approach is injecting a mock MatDialog provider that has a jasmine spy for the close or open methods. For instance:

import { MyDialogComponent } from './mydialog.component';
import { async, TestBed, inject } from '@angular/core/testing';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { MatDialog } from '@angular/material/dialog';

describe('MyDialogComponent', () => {
  const mockDialogRef = {
    close: jasmine.createSpy('close')
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [MyDialogComponent],
      imports: [MatDialogModule],
      providers: [
        {
          provide: MatDialogRef,
          useValue: mockDialogRef
        }
      ]
    })
    .compileComponents();
  }));

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('#onClosedeleteCustomer should close the dialog', () => {
    component.onClosedeleteCustomer();
    expect(mockDialogRef.close).toHaveBeenCalled();
  });
});

This gist is, don't inject the real instance of the MatDialog or MatDialogRef in your test. Inject a mock object instead with a jasmine spy that can tell you if a method has been called.

12
votes

When I was trying to test the dialog component I encountered the same issue. My solution is based on the dialog test in angular-material source code.

import { MyDialogComponent } from './mydialog.component';
import { async, TestBed, inject } from '@angular/core/testing';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatDialog } from '@angular/material/dialog';
import { OverlayContainer } from '@angular/cdk/overlay';

describe('MyDialogComponent', () => {
  let dialog: MatDialog;
  let overlayContainer: OverlayContainer;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [MyDialogComponent],
      imports: [
        MatDialogModule,
      ]
    });

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

    TestBed.compileComponents();
  }));

  beforeEach(inject([MatDialog, OverlayContainer],
    (d: MatDialog, oc: OverlayContainer) => {
      dialog = d;
      overlayContainer = oc;
    })
  );

  afterEach(() => {
    overlayContainer.ngOnDestroy();
  });

  it('should open a dialog with a component', () => {
    const dialogRef = dialog.open(MyDialogComponent, {
      data: { param: '1' }
    });

    // verify
    expect(dialogRef.componentInstance instanceof MyDialogComponent).toBe(true);
  });
});

I'm not sure this is the right approach though, still a beginner myself.

0
votes

I've added the MatDialogRef to the provider in the module

@NgModule({
  declarations: [
    AppComponent,
   .
   .
    other components...
   .
   .
  ],
  imports: [
    BrowserModule,
  ],
  providers: [
              { 
               provide: MatDialogRef 
              }
             ],
  bootstrap: [AppComponent]
})
export class AppModule { }