angular: Dependency injection and class inheritance doesn't work in Angular 8 library test
🐞 bug report
Affected Package
The issue is caused by package @angular/core@^8
Is this a regression?
Yes, the previous version in which this bug was not present was: @angular/core@7
Description
The problem occurs when:
- The base class has a constructor with an injected service. (
Injector
from the example)
@Injectable()
export class BaseService {
// "Injector" is just for the sake of example. Any injectable would have the same problem.
constructor(public injector: Injector) {
}
}
- Child class has no constructor (i.e., it relies on base class constructor) AND at least one property with the default value.
@Injectable()
export class MyLibService extends BaseService {
public foo = 'bar';
}
- (dont ask me how I have found this, but:) In
angular.json
[lib name].test.options.codeCoverage
should be true.
PROBLEM:
injector
is undefined in the instance of MyLibService
.
🚩 Removing property from the child class (2) AND/OR opting out codeCoverage
config in angular.json (3) doesn’t brake test.
As a result, I can’t have a class inheritance and code coverage at the same time.
I could put a constructor in the child class and call super
by passing all required arguments manually, but it’s the last option since the real-life scenario is much more complicated than the example provided here.
🔬 Minimal Reproduction
git repo: https://github.com/ddramone/ng-8-library-inheritance-di-test-bug
reproduce steps:
- Install angular 8 cli
- Clone
git clone https://github.com/ddramone/ng-8-library-inheritance-di-test-bug.git
npm i
- run Test
ng test my-lib
Expected behavior
Test should pass = Injector service should be available in the service instance.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 32
- Comments: 17 (4 by maintainers)
Commits related to this issue
- test(core): check dependency in extended child (#34767) Related to https://github.com/angular/angular/issues/31337 PR Close #34767 — committed to angular/angular by KostyaTretyak 4 years ago
- test(core): check dependency in extended child (#34767) Related to https://github.com/angular/angular/issues/31337 PR Close #34767 — committed to angular/angular by KostyaTretyak 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 4 years ago
- fix(compiler): use base class parameters for any non-decorated class without constructor parameters Previously, the JIT compiler would take into consideration whether a type had a constructor functio... — committed to JoostK/angular by JoostK 3 years ago
- fix(compiler): use base class parameters for any non-decorated class without constructor parameters Previously, the JIT compiler would take into consideration whether a type had a constructor functio... — committed to JoostK/angular by JoostK 3 years ago
- fix(compiler): use base class parameters for any non-decorated class without constructor parameters Previously, the JIT compiler would take into consideration whether a type had a constructor functio... — committed to JoostK/angular by JoostK 3 years ago
- fix(compiler): use base class parameters for any non-decorated class without constructor parameters Previously, the JIT compiler would take into consideration whether a type had a constructor functio... — committed to JoostK/angular by JoostK 3 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 2 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 2 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 2 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. Thes... — committed to JoostK/angular by JoostK 2 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled (#44732) When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting stateme... — committed to angular/angular by JoostK 2 years ago
- fix(compiler): properly compile DI factories when coverage reporting is enabled (#44732) When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting stateme... — committed to angular/angular by JoostK 2 years ago
I have looked into why DI doesn’t work when coverage reporting is enabled, and it can be traced back to how Angular detects whether a constructor was present in a class, or if a constructor is generated synthetically, e.g. to initialize class fields. Enabling coverage inserts coverage instrumentation statements into the generated constructors:
These prevent Angular from recognizing the constructor as synthesized, causing
MyLibService
to be instantiated with 0 arguments. The proper behavior would be to delegate to the parent constructor, allowing its dependencies to be injected.I opened #37811 as a proposal to workaround this issue. It’s not done yet and I can’t give an ETA yet, unfortunately.
tested on angular 8.2.11 with cli 8.3.12 and it is still not fixed, now I have to add tons of workarounds 😦
we are seeing the same issue when using differential loading with v8 and this is breaking in es5 script bundles in IE 11 for specific instances where we have multiple class inheritances without constructor defined in some of the classes.
ex: ` export class A { constructor(public dep1: SomeType){} }
export class B extends A { }
export class C extends B { constructor(dep1: SomeType, dep2: AnotherType){ super(dep1); }
ngOnInit(){ //this.dep1 is undefined here //this.dep2 is defined } } `
I got the same problem. It appears only if tests are running with code coverage:
ng test --code-coverage
The usualng test
or any type of build (JIT or AOT) gives no errors.For now the problem can be solved by injecting services on child level and passing them to parent component.