angular: ComponentFactoryResolver is not aware of components compiled via TestBed

The ComponentFactoryResolver has an internal map containing factories for components that have been compiled.

However, when TestBed compiles components declared in the DynamicTestModule, the factories are stored in the TestBed’s internal _moduleWithComponentFactories. These factories are never made known to the ComponentFactoryResolver, so any tests that use the resolver to create a component declared in the test module will fail with Error: No component factory found for YourTestComponent.

As a workaround, you can simply create a real @NgModule in which to declare your test components and import that into the test module.

cc @tbosch @IgorMinar

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 22
  • Comments: 36 (10 by maintainers)

Commits related to this issue

Most upvoted comments

We should probably add entryComponents to configureTestingModule. Then the workaround you mentioned above of creating a temporary NgModule would not be needed any more…

Please, do. It’s very inconvenient to configure entryComponents in tests now. I use this workaround at the moment (not sure if it’s correct way, but it works at least):

TestBed.configureTestingModule({
  declarations: [ MyDynamicComponent ],
});

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

They are not treated as entryComponents, otherwise the ComponentFactoryResolver would know them.

The ComponentFactoryResolver is only using entryComponents. The TestBed.createComponent method is not using the ComponentFactoryResolver nor the entryComponents of any module.

We should probably add entryComponents to configureTestingModule. Then the workaround you mentioned above of creating a temporary NgModule would not be needed any more… On Thu, Aug 18, 2016 at 5:35 PM Jeremy Elbourn notifications@github.com wrote:

@tbosch https://github.com/tbosch configureTesitngModule doesn’t have entryComponents- all of its declarations are automatically treated as entryComponents.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/10760#issuecomment-240761942, or mute the thread https://github.com/notifications/unsubscribe-auth/AAqKf6tgWiEAFIAc8zakZ9V6g8W_v3deks5qhHuegaJpZM4Jjfor .

I had meant to say that the declarations of the test module are meant to be treated as entryComponents, and the fact that they’re not causes this bug. @IgorMinar is that the intention with the testing module?

@tbosch configureTesitngModule doesn’t have an entryComponents property- all of its declarations are automatically treated as entryComponents.

Can anyone share an example of how to test the MdDialog please… I am having issues testing the mdDialog with my code. I tried the looking into the code of Angular material 2 but I cant get it to work (maybe I am not understanding the example).

Needs investigation and we will propose a solution here.

It worked in the end for me @LOZORD

Here is a full gist of it, using a shared module pattern.

https://gist.github.com/ReneVallecillo/24e980c2a8442d9daaf187debd2d4369

Maybe @fallXone could use it.

Ignore js extension, it should be ts but it seems gist has no support of typescript

Importing MdDialogModule should fix that error.

This worked for me:

@NgModule({
    declarations: [TestComponent],
    imports: [MdDialogModule], // <-- added this line
    entryComponents: [TestComponent],
    exports: [TestComponent],
})
class TestModule { }

@renevall did you tried like below?

in your configureTestingModule,

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

I have a very similar issue in where I need to declare a component dynamically within a featured module. My featured module which is loaded async when route is visited looks as follows:

@NgModule({
  imports: [
    SharedModule,
    FeaturedRoutingModule,
  ],
  declarations: [
    FeaturedComponent,
    DynamicComponent
  ],
  entryComponents: [ DynamicComponent]
})
export class FeaturedModule {
  constructor(private builder: BuilderService) {
    // This just notifies the builder of the new component it is able to build.
    builder.mapComponent('dynamic_component', DynamicComponent);
  }
}

I have another SharedModule where I import most of my site wide components most of them are entryComponents. I then have a Builder Service that is able to dynamically create a factory for each entryComponents using the ComponentFactory which works great for all components defined in the Shared and Main module but for the DynamicComponent that is only loaded with the dynamic module I get No component factory found for DynamicComponent. Did you add it to @NgModule.entryComponents? even though it is obviously defined in the entryComponents. Using the component statically in the FeaturedComponent template works but not when trying to create it dynamically.

@LOZORD Could you provide a code, I keep getting

Error: No provider for MdDialogRef!

@allenhwkim For dynamically creating a component with the ComponentFactoryResolver you need to have the component declared in the entryComponents section. At least that’s my understanding and my tests confirmed this.

Having to declare the components I want to dynamically create in the @NgModule’s entryComponents section is a huge regression from my point of view. I used the DynamicComponentLoader and then the ComponentResolver and they both worked like a charm, but this new way of doing it it’s really not dynamic anymore and kind of beats the purpose if I have to “hardcode” the list of the components I want to dynamically create… this really broke our app, big way…

Tests that rely on ComponentFactoryResolver to be filled correctly need to declare “entryComponents” via configureTestModule (or import a module that does so). This way you can test that your module is correct / test how users would use your module.

The internal map in the test module should never go into the ComponentFactoryResolver as it makes all components entryComponents, which is not the case in actual apps.

On Sat, Aug 13, 2016 at 12:00 AM Jeremy Elbourn notifications@github.com wrote:

The ComponentFactoryResolver has an internal map https://github.com/angular/angular/blob/b96869afd2bdd3ad8c5b7477da647af628e383aa/modules/%40angular/core/src/linker/component_factory_resolver.ts#L40 containing factories for components that have been compiled.

However, when TestBed compiles components declared in the DynamicTestModule, the factories are stored in the TestBed’s internal _moduleWithComponentFactories https://github.com/angular/angular/blob/master/modules/@angular/core/testing/test_bed.ts#L141. These factories are never made known to the ComponentFactoryResolver, so any tests that use the resolver to create a component declared in the test module will fail with Error: No component factory found for YourTestComponent.

As a workaround, you can simply create a real @NgModule in which to declare your test components and import that into the test module.

cc @tbosch https://github.com/tbosch @IgorMinar https://github.com/IgorMinar

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/10760, or mute the thread https://github.com/notifications/unsubscribe-auth/AAqKf7f0Dc5XkKQk0pHf-u-_kay98yB2ks5qfO0AgaJpZM4Jjfor .