44

I have a DialogComponent that has the following constructor where Dialog is a custom object:

constructor(
    public dialogRef: MatDialogRef<CustomDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data: Dialog
)

I created the following TestBed in Angular4:

data = new Dialog()
data.message = 'Dialog Message'

TestBed.configureTestingModule({
    imports: [MaterialModules],
    declarations: [CustomDialogComponent],
    providers: [MatDialogRef, { provide: Dialog, useValue: data }]
})

TestBed.overrideModule(BrowserDynamicTestingModule, {
    set: {
        entryComponents: [CustomDialogComponent]
    }
})
await TestBed.compileComponents()

But I get the following error:

Failed: Can't resolve all parameters for MatDialogRef: (?, ?, ?).
Error: Can't resolve all parameters for MatDialogRef: (?, ?, ?).

changing providers to:

providers: [
    { provide: MatDialogRef, useValue: {} },
    { provide: MAT_DIALOG_DATA,  useValue: data }
]

results in the following error:

Error: No provider for Dialog!

How do I resolve this?

PaulBunion
  • 346
  • 2
  • 18
suku
  • 10,507
  • 16
  • 75
  • 120

5 Answers5

48

I solved it by changing the component constructor to:

constructor(
  public dialogRef: MatDialogRef<CustomDialogComponent>,
  @Inject(MAT_DIALOG_DATA) public data: Dialog | any
)

The providers in the TestBed were:

providers: [{ provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: data }]
suku
  • 10,507
  • 16
  • 75
  • 120
  • 1
    No reason why this would work. Please consider adding a reason if it did indeed do something. However, the order does not matter. – tinyBIGideas Jul 13 '18 at 14:08
  • Because it is a types issue. Dialog is not of type MAT_DIALOG_DATA which is needed in providers for test. Hence, I had to allow data to take 'any' which then can accept MAT_DIALOG_DATA type also. Please read the question and the answer properly. Look at the inject statement. – suku Jul 14 '18 at 02:13
  • 7
    How do I test multiple cases of data? – FizxMike Sep 10 '18 at 14:51
  • Found answer for swapping out provider value at test time: https://github.com/angular/quickstart/issues/320#issuecomment-315866672 – FizxMike Sep 10 '18 at 15:01
15

If you use at least one MatDialogRef method, you should create a mock. For example I use the close() method. Without it errors would be generated so I made the below class with an empty method.

export class MatDialogRefMock {
    close(value = '') {

    }
}

and use that instead of an empty value, with useClass

{ provide: MatDialogRef, useClass: MatDialogRefMock },
Sen Alexandru
  • 1,953
  • 3
  • 19
  • 35
  • 8
    You can also provide a factory that generates a spyObj, so for each new test, you will have a fresh spy: `{ provide: MatDialogRef, useFactory: () => jasmine.createSpyObj('MatDialogRef', ['close', 'afterClosed']) }` – Rafael Cerávolo Oct 27 '20 at 22:43
  • 1
    That's the right answer! – Deian Sep 27 '21 at 13:59
10

You can do this by the help of jasmine spy, as follow:

Step 1: Define the mock data

  const data = {
    title: 'title',
    message: 'message',
    summary: 'my summary'
  };

Step 2: Create jasmine spy for MatDialogRef (Write this inside beforeEach block)

const matDialogSpy = jasmine.createSpyObj('MatDialogRef', ['onNoClick', 'closeDialog']);

onNoClick and closeDialog are methods inside MessagePopupComponnet

Step 3: Configure the TestBed as follow:

  providers: [
    {
      provide: MatDialogRef,
      useValue: matDialogSpy
    },
    {
      provide: MAT_DIALOG_DATA,
      useValue: data
    }
  ]

For the ref, I've provided the full code below:

import { DebugElement } from "@angular/core";
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";

import { MessagePopupComponent } from "./message-popup.component";


describe('MessagePopup', () => {
  let fixture: ComponentFixture<MessagePopupComponent>;
  let component: MessagePopupComponent;
  let el: DebugElement;
  let matDialogRef;

  const data = {
    title: 'title',
    message: 'message',
    summary: 'my summary'
  };

  beforeEach(async(() => {

    const matDialogSpy = jasmine.createSpyObj('MatDialogRef', ['onNoClick', 'closeDialog']);

    TestBed.configureTestingModule({
      declarations: [MessagePopupComponent],
      providers: [
        {
          provide: MatDialogRef,
          useValue: matDialogSpy
        },
        {
          provide: MAT_DIALOG_DATA,
          useValue: data
        }
      ]
    }).compileComponents()
    .then(() => {
      fixture = TestBed.createComponent(MessagePopupComponent);
      component = fixture.componentInstance;
      el = fixture.debugElement;
      matDialogRef = TestBed.get(MatDialogRef);
    });

  }));

  fit('should create Message Popup component', () => {
    expect(component).toBeTruthy('Message popup not created')
  });

});
Anand Raja
  • 2,676
  • 1
  • 30
  • 35
6

Import MatDialogModule and MatDialogRef from angular/material/dialog instead of angular/material. Import the ModalDialogModule and provide providers for MatDialogRef in your TestBed.

Import {MatdialogModule,MatDialogRef} from '@angular/material/dialog';

TestBed.configureTestingModule({
declarations: [componentName],
imports: [MatdialogModule],
providers: [{provide : MatDialogRef, useValue : {}}]
});
Nandita Sahu
  • 89
  • 1
  • 7
2

To solve this problem, you can do the following steps:

1- You used the data as input to the dialog and used it in the constructor to set the variables. (in .ts)

  constructor(public dialogRef: MatDialogRef<any>,
          @Inject(MAT_DIALOG_DATA) public data: any) {
     this.ip= data.ip;
     this.name= data.name;
  }

2- We should create fake data, (in .spec.ts)

const data = {
    ip: '1.1.1.1',
    name: 'test'
 }

3- We define provider as follows And we introduce data, (in .spec.ts)

 providers: [
   {provide: MatDialogRef, useValue: {}},
   {provide: MAT_DIALOG_DATA, useValue: data},
  ]

4- full code in .spec.ts

import {async, ComponentFixture, TestBed} from '@angular/core/testing';

import {CustomDialogComponent} from './backup-list.component';
import {TranslateModule} from '@ngx-translate/core';
import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from '@angular/material';
import {BrowserDynamicTestingModule} from '@angular/platform-browser-dynamic/testing';
import {NO_ERRORS_SCHEMA} from '@angular/core';

const data = {
    ip: '1.1.1.1',
    name: 'test'
 };

describe('BackupListComponent', () => {
   let component: BackupListComponent;
   let fixture: ComponentFixture<BackupListComponent>;

    beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [CustomDialogComponent],
        imports: [
            TranslateModule.forRoot(),
            MatDialogModule,
            BrowserDynamicTestingModule
        ],
        providers: [
            {provide: MatDialogRef, useValue: {}},
            {provide: MAT_DIALOG_DATA, useValue: data},
        ],
        schemas: [NO_ERRORS_SCHEMA]
    })
        .compileComponents();
  }));
 });
Mahdi
  • 307
  • 6
  • 19