I got some problems with my tests.
In my case, I have 2 components:
import {Component, Input, OnInit} from '@angular/core';
import {BComponent} from '../b/b.component';
@Component({
selector: 'a',
templateUrl: './a.component.html',
styleUrls: ['./a.component.scss'],
providers: [{provide: BComponent, useExisting: AComponent}]
})
export class AComponent extends BComponent<any> implements OnInit {
@Input() foo = true;
ngOnInit(): void {
super.ngOnInit();
}
}
import {Component, Input, OnInit} from '@angular/core';
import {FormBuilder} from '@angular/forms';
@Component({
template: '',
})
export abstract class BComponent<T> implements OnInit {
@Input() value: T;
constructor(
protected formBuilder: FormBuilder
) {
let a = this.formBuilder.control(null);
}
ngOnInit(): void {
}
}
My test looks like this:
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {AComponent} from './a.component';
import { ReactiveFormsModule} from '@angular/forms';
describe('AComponent', () => {
let component: AComponent;
let fixture: ComponentFixture<AComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AComponent],
imports: [
ReactiveFormsModule],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
The problem is the @Input() foo = true;
in AComponent
.
There must be some problems with the default value and ReactiveFormsModule
.
Error shows:
AComponent › should create
TypeError: Cannot read property 'control' of undefined
12 | protected formBuilder: FormBuilder
13 | ) {
> 14 | let a = this.formBuilder.control(null);
| ^
15 |
16 | }
17 |
at new BComponent (src/app/testing/brainfuck/b/b.component.ts:14:34)
at new AComponent (src/app/testing/brainfuck/a/a.component.ts:258:5)
at createClass (../packages/core/src/view/provider.ts:273:14)
at createDirectiveInstance (../packages/core/src/view/provider.ts:142:7)
at createViewNodes (../packages/core/src/view/view.ts:314:28)
at createRootView (../packages/core/src/view/view.ts:216:3)
at callWithDebugContext (../packages/core/src/view/services.ts:641:23)
at Object.debugCreateRootView [as createRootView] (../packages/core/src/view/services.ts:121:10)
at ComponentFactory_.Object.<anonymous>.ComponentFactory_.create (../packages/core/src/view/refs.ts:91:27)
at initComponent (../packages/core/testing/src/test_bed.ts:605:28)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:407:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3765:43)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:406:56)
at Object.onInvoke (../packages/core/src/zone/ng_zone.ts:405:29)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:406:56)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:167:47)
at NgZone.Object.<anonymous>.NgZone.run (../packages/core/src/zone/ng_zone.ts:184:50)
at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine.createComponent (../packages/core/testing/src/test_bed.ts:609:56)
at Function.Object.<anonymous>.TestBedViewEngine.createComponent (../packages/core/testing/src/test_bed.ts:236:36)
at src/app/testing/brainfuck/a/a.component.spec.ts:20:27
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:407:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3765:43)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:406:56)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:167:47)
at Object.wrappedFunc (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:4250:34)
If I remove the default value, everything works fine.
Angular version is
"@angular/core": "12.0.5",
Thanks in advance!
CodePudding user response:
When you add a default property value on AComponent
it's forcing the inherited constructor to be called. This occurs because class properties that have an initial value are effectively moved into the constructor during Typescript transpilation
You probably shouldn't have a constructor on the abstract class, but at the very least you'll need to call it by overriding that constructor in AComponent
, passing the FormBuilder
as a param
constructor(
protected formBuilder: FormBuilder
) {
super(formBuilder)
}
Note: I didn't get a chance to test this out, but there may be some (other) unexpected behavior with constructors on abstract classes