angular icon indicating copy to clipboard operation
angular copied to clipboard

TestBed overridden providers always registered in NgModule injector

Open JoostK opened this issue 5 years ago • 1 comments

🐞 bug report

Affected Package

The issue is caused by package @angular/core/testing

Is this a regression?

Sort of; changed behavior in Ivy compared to VE.

Description

In #28737 the following testcase was added to exercise ngOnDestroy logic registered for component-level provided providers that have been overridden:

   it('should call ngOnDestroy for a service that was overridden with useFactory', () => {
     const logs: string[] = [];

     @Injectable()
     class MyService {
       public name = 'MyService';
       ngOnDestroy() {
         logs.push('MyService.ngOnDestroy');
       }
     }

     class FakeService {
       name = 'FakeService';
       ngOnDestroy() {
         logs.push('FakeService.ngOnDestroy');
       }
     }

     @Component({
       selector: 'my-comp',
       template: `{{ myService.name }}`,
       providers: [MyService],
     })
     class MyComponent {
       constructor(public myService: MyService) {}
     }

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

     TestBed.overrideProvider(MyService, {
       useFactory: () => new FakeService(),
     });

     const fixture = TestBed.createComponent(MyComponent);
     fixture.detectChanges();

     const service = TestBed.inject(MyService);
     expect(service.name).toBe('FakeService');

     fixture.destroy();

     expect(logs).toEqual(['FakeService.ngOnDestroy']);
   });

In this test, the MyService token is provided by the component and overridden using TestBed.overriddeProvider. In Ivy, the TestBed.inject(MyService); call succeeds and returns an instance of FakeService. In VE, this call fails as there is no MyService token registered in the NgModule injector.

The VE behavior seems logical to me, as the service wasn't originally provided in the NgModule injector and only registering an override should not register it at the NgModule level.

In Ivy, all provider overrides are registered in the NgModule injector:

https://github.com/angular/angular/blob/44bb85ade456a8223851b3b376fe7f5c8fe0864a/packages/core/testing/src/r3_test_bed_compiler.ts#L660-L665

🔬 Minimal Reproduction

See testcase above.

🌍 Your Environment

Angular Version: Latest master, 11.0.0-next.4

JoostK avatar Oct 01 '20 15:10 JoostK

This issue affects at least one g3 target (I'm surprised that it's just one), more info in cl/351872802.

The workaround for this issue is to move the component level providers into the associated NgModule.

IgorMinar avatar Jan 14 '21 22:01 IgorMinar