angular-cli: Injecting a service into a Component using TestBed.overrideComponent no longer works

Bug Report or Feature Request (mark with an x)

- [x ] bug report -> please search issues before submitting
- [ ] feature request

Versions.

@angular/cli: 1.4.4 node: 6.10.0 os: win32 x64 @angular/animations: 4.4.4 @angular/cdk: 2.0.0-beta.11 @angular/common: 4.4.4 @angular/compiler: 4.4.4 @angular/core: 4.4.4 @angular/forms: 4.4.4 @angular/http: 4.4.4 @angular/material: 2.0.0-beta.11 @angular/platform-browser: 4.4.4 @angular/platform-browser-dynamic: 4.4.4 @angular/router: 4.4.4 @angular/cli: 1.4.4 @angular/compiler-cli: 4.4.4 @angular/language-service: 4.4.4 typescript: 2.3.4

Repro steps.

I am injecting a service into a component using the @Component annotation / providers property:

@Component({
    providers: HelloWorldService
})
export class HelloWorldComponent {
    constructor(private helloWorldService: HelloWorldService) {}
}

After updating to @angular/cli@1.4.4, using the TestBed’s overrideComponent function to provide the service throws an error:

Error: No provider for HelloWorldService!

TestBed.configureTestingModule({
    declarations: [
        HelloWorldComponent
    ]
}).overrideComponent(HelloWorldComponent, {
    set: {
        providers: [
            { provide: HelloWorldService, useFactory: helloWorldService => new HelloWorldService(null) }
        ]
    }
}).compileComponents().then(() => {
    // ...
});

Providing the service using the configureTestingModule function also throws an error:

TestBed.configureTestingModule({
    declarations: [
        HelloWorldComponent
    ],
    providers: [
        { provide: HelloWorldService, useFactory: helloWorldService => new HelloWorldService(null) }
    ]
}).compileComponents().then(() => {
    // ...
});

No provider for Http!

I can only get the component to compile if I provide the service in both the configureTestingModule function and the overrideComponent function.

The log given by the failure.

Desired functionality.

Mention any other details that might be useful.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 15 (8 by maintainers)

Most upvoted comments

The TestBed only knows about it’s providers. The component has it’s own injector as well. If you were to use the component way of providing your service, use the following:

.then(() => {
  const componentFixture = TestBed.createComponent(HelloWorldComponent);
  const service = componentFixture.componentRef.injector.get(HelloWorldService);
});

Providers ideally should be provided at the NgModule level, not the individual component level. Again, ideally. Should you however provide it on the component, here’s the passing code to make it work:

  beforeEach(async(() => {
    TestBed
      .configureTestingModule({
        declarations: [HelloWorldComponent]
      })
      .overrideComponent(HelloWorldComponent, {
        set: {
          providers: [
            { provide: HelloWorldService, useValue: new HelloWorldService(null) }
          ]
        }
      })
      .compileComponents();
  }));

But if you use the NgModule way (edited for brevity):

app.module.ts

@NgModule({
...
  providers: [
    HelloWorldService
  ],
...
})
export class AppModule { }

hello-world.component.ts

@Component({
  selector: 'app-hello-world',
  templateUrl: './hello-world.component.html',
  styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {

hello-world.component.spec.ts

TestBed
  .configureTestingModule({
    providers: [
      { provide: HelloWorldService, useValue: new HelloWorldService(null) }
    ],
    declarations: [HelloWorldComponent]
  })
  .compileComponents();

@chris-jones-pixitmedia,

This looks to be an issue with the way you are testing, not the CLI.

This issue tracker is not suitable for support requests, please repost your issue on StackOverflow using tag angular-cli.

If you are wondering why we don’t resolve support issues via the issue tracker, please check out this explanation.

That said, try this:

beforeEach(async(() => {
  TestBed
    .configureTestingModule({
      providers: [
        { provide: HelloWorldService, useValue: new HelloWorldService(null) }
      ],
      declarations: [HelloWorldComponent],
    })
    .compileComponents();
}));

Closing this as fixed.

There are actually two injectors in play. The TestBed injector (acting like NgModule’s injector) and the component’s injector. TestBed.get() uses the providers array in configureTestingModule while the componentRef.injector is using the component’s injector which uses the component level providers, which is overrode by the set in overrideComponent.