30

I'm trying to teach myself how to code with Angular and I have a problem. I am creating a app for myself and I have just implemented the Angular Material Dialog. I put this in a wrapper service and all seems well. So in a component I call the Wrapper Service to raise a modal like so...

public assignInstrument(instrument: any): void {
    this.modalDialogWrapperService.openAssignmentWindow({
        product: 'instrument',
        type: instrument.type,
        serial: instrument.serial,
        id: instrument.id
  });
}

the service method looks like so, notice I pass the name of a component I wish to raise in the modal window

openAssignmentWindow(instrument) {
    const dialogRef = this.dialog.open(ChangeAssignmentComponent, {
        data: instrument,
        width: '693px',
        height: '498px'
        });
    dialogRef.afterClosed().subscribe(() => {});
    });
}

Everything works great! But as a good developer I should write unit tests... so to test my component I have the following test (I have included how I mock the Service and some other code to give an impression of the test file)

let modalDialogWrapperServiceSpy: jasmine.SpyObj<ModalDialogWrapperService>;
    const mockModalDialogWrapperService = jasmine.createSpyObj('ModalDialogWrapperService', ['openAssignmentWindow']);
    mockModalDialogWrapperService.openAssignmentWindow.and.returnValue({});

TestBed.configureTestingModule({
    imports: [...],
    declarations: [...],
    providers: [{
      provide: ModalDialogWrapperService,
      useValue: mockModalDialogWrapperService
    }]
}).compileComponents();

beforeEach(() => {
    fixture = TestBed.createComponent(InstrumentsPageComponent);
    modalDialogWrapperServiceSpy = TestBed.get(ModalDialogWrapperService);
    component = fixture.componentInstance;
    fixture.detectChanges();
 })

describe('assignInstrument', () => {
  it('should call the Modal Dialog Service', () => {
    component.assignInstrument({});
    expect(modalDialogWrapperServiceSpy.openAssignmentWindow).toHaveBeenCalledTimes(1);
  });
});

This one test fails! With the error "Error: No component factory found for ChangeAssignmentComponent. Did you add it to @NgModule.entryComponents" - this seems odd as in my app.module file I declare "ChangeAssignmentComponent" in entryComponents and declarations arrays? I am confused - does anyone know what I could be doing wrong?

NewToAngular
  • 975
  • 2
  • 13
  • 23
  • Have you imported the `module` in your spec in which your component is `declared` and added as `entryComponents`? – Amit Chigadani Mar 04 '19 at 09:41
  • There is only one module in my app, so I don't declare it in the imports array of my testbed for example: `imports: [MatTableModule, MatPaginatorModule, MatDialogModule, NoopAnimationsModule],` – NewToAngular Mar 04 '19 at 09:53
  • Possible duplicate of [Providing "entryComponents" for a TestBed](https://stackoverflow.com/questions/41483841/providing-entrycomponents-for-a-testbed) – Amit Chigadani Mar 04 '19 at 10:05

3 Answers3

53

Testing is doubting.

More seriously, let me answer you.

In Angular, your components are handled by a module. When you use Material dialogs and snackers, you actually use a feature of the CDK, which is called Portal. This allow you to create your components dynamically.

But when you do so, you have to add them to the entryComponents of your module. You did it in your module, so you should also do it in your tests.

The syntax is

TestBed
  .configureTestingModule(...)
  .overrideModule(BrowserDynamicTestingModule, { set: { entryComponents: [YourComponent] } });
nPcomp
  • 8,637
  • 2
  • 54
  • 49
  • 1
    When I added the code above I got the following error "Failed: Component ChangeAssignmentComponent is not part of any NgModule or the module has not been imported into your module." – NewToAngular Mar 04 '19 at 09:54
  • 2
    Did you add the said component tou your testbed module configuration ? –  Mar 04 '19 at 09:57
  • Oh I don't think so, would this be in the declarations array? – NewToAngular Mar 04 '19 at 10:03
  • @NewToAngular exactly –  Mar 04 '19 at 10:07
  • It seems to have sorted the issue, but my test fails "Expected spy ModalDialogWrapperService.openAssignmentWindow to have been called once. It was called 0 times." so I still have more to fix, but the import issue is now resolved - thanks – NewToAngular Mar 04 '19 at 10:14
  • Well I can't tell why, your code seems fine. If you can, provide a [mcve] in a new question, and people will be able to help you out ! –  Mar 04 '19 at 10:16
  • (@NewToAngular except that you don't have a `modalDialogWrapperServiceSpy` in your code) –  Mar 04 '19 at 10:17
  • Sorry, it is there, I declare it at the top of the test (I have updated the code) – NewToAngular Mar 04 '19 at 10:21
  • THe answer of Ajay Reddy should have been accepted as the correct one, not yours. :) Could you please edit your answer and add information that you have to mention your component inside of "configureTestingModule -> declarations" array along with "overrideModule -> entryComponents" array? – Pasha Dec 02 '19 at 21:34
  • Worked for me changing `BrowserDynamicTestingModule` for `BrowserAnimationsModule` – adrisons Oct 01 '21 at 08:17
20

there are two places at which this is supposed to be done....entry components and also at declarations(while configuring your testing module)....

  TestBed
  .configureTestingModule({
              declarations: [YourComponent],
   })
  .overrideModule(BrowserDynamicTestingModule, { set: { entryComponents: [YourComponent] } });
Ajay Reddy
  • 1,475
  • 1
  • 16
  • 20
  • 1
    Your answer should have been accepted as the answer to the topic. Thank you it was helpful to me. – Pasha Dec 02 '19 at 21:34
5

If someone struggling to find BrowserDynamicTestingModule just use BrowserModule instead.

   TestBed
  .configureTestingModule(...)
  .overrideModule(BrowserModule, { set: { entryComponents: [YourComponent] } });
  • You can import `BrowserDynamicTestingModule` like this: `import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';` – Guillaume Nury Mar 24 '21 at 11:01