Dependency injection and class inheritance doesn't work in Angular 8 library test
See original GitHub issue🐞 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.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:32
- Comments:17 (4 by maintainers)
Top GitHub Comments
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 😦