Mocking a parent FormGroup via @input in Jasmine
I was able to setup a successful Karma spec test for a Reactive Form Parent <-> Child component. Hopefully the example below will help guide your setup. I've simplified as much code from my codebase to focus on the core question you're trying to resolve.
Parent Component
parent.component.html
...
<div [stepControl]="childFormGroup">
<child-form-group [formGroup]="childFormGroup"></child-form-group>
</div>
...
parent.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
@Component({
selector: 'form-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss']
})
export class FormParentComponent implements OnInit {
// childFormGroup will be available on the parent DOM
// so we can inject it / pass it to the ChildFormComponent
public childFormGroup : FormGroup;
constructor(private _formBuilder: FormBuilder) {
this.createForm();
}
private createForm() : void {
this.childFormGroup = this._formBuilder.group({
name: ['Sample Name', Validators.required],
email: ['', Validators.required]
});
}
}
Child Component
child.component.html
...
<form [formGroup]="formGroup">
<p>This is the childFormGroup</p>
<br>
<div>
<input placeholder="Name"
formControlName="name"
autocomplete="off">
</div>
<div>
<input placeholder="Email"
formControlName="email"
autocomplete="off">
</div>
</form>
...
child.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'child-form-group',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss'],
})
export class ChildFormComponent {
// This declares an inherited model available to this component
@Input() formGroup : FormGroup;
constructor() { }
/* There is no need to create the formGroup here
hence no constructor method call or ngOnInit() hook...
It will simply inherit the formGroup by passing it as an
attribute on the DOM from parent.component.html
*/
}
child.component.spec.ts
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormsModule, ReactiveFormsModule, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ChildFormComponent } from './child.component';
describe('ChildFormComponent', () => {
let component: ChildFormComponent;
let fixture: ComponentFixture<ChildFormComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
schemas: [
CUSTOM_ELEMENTS_SCHEMA
],
imports: [
FormsModule,
ReactiveFormsModule
],
declarations: [
ChildFormComponent
]
})
.compileComponents();
}));
beforeEach(inject([FormBuilder], (fb: FormBuilder) => {
fixture = TestBed.createComponent(Step2Component);
component = fixture.componentInstance;
/* This is where we can simulate / test our component
and pass in a value for formGroup where it would've otherwise
required it from the parent
*/
component.formGroup = fb.group({
name: ['Other Name', Validators.required],
email: ['', Validators.required]
});
fixture.detectChanges();
}));
it('should create', () => {
expect(component).toBeTruthy();
});
});
anonuser1234
Updated on June 21, 2022Comments
-
anonuser1234 about 2 years
So I have a child component that goes something like this
export class ChildComponent implements OnInit { @Input('parentForm') public parentForm: FormGroup; constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { } ngOnInit() { this.parentForm.addControl('newControl', <Some Control>); } }
Next I have a barebones unit testing file that goes like this
describe('ChildComponent', () => { let component: ChildComponent; let fixture: ComponentFixture<ChildComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ReactiveFormsModule, FormsModule], declarations: [ ChildComponent ], providers: [ FormBuilder, FormGroup ] }) .compileComponents(); })); beforeEach(inject([FormBuilder], (fb: FormBuilder) => { fixture = TestBed.createComponent(ChildComponent); component = fixture.componentInstance; component.parentForm = fb.group({}); component.ngOnInit(); fixture.detectChanges(); })); fit('should be created', () => { expect(component).toBeTruthy(); }); });
Previously I had an issue where parentForm was undefined so I tried to build it myself by doing injecting FormBuilder in the beforeEach by doing this
component.parentForm = fb.group({});
. However now the issue is that karma/jasmine cannot find FormBuilderCannot find name 'FormBuilder'.
All I am trying to do is try to get or mock the parentForm for when I create an instance of the component during my unit testing, and I need it because I am calling ngOnInit during the for each as it as a new control.
Any ideas. Thank you
-
James Young over 5 yearsThis is awesome. Thanks! Fyi missing a second closing parenthesis at the end of the 2nd beforeEach...
-
Pixxl over 5 years@JamesYoung Thanks for letting me know, and glad this helped you! :)
-
anonuser1234 over 5 yearsAlthough I am no longer doing this type of coding I appreciated your answer! Wish I found this back when I had the problem haha
-
Pixxl over 5 yearsAh well it's unfortunate I came upon this question late! Hopefully it helps someone else in the future though. I was running into the same issues so this let me work through it and I learned how to fix it myself.
-
Till Kuhn over 3 years@Pixxl it just helped me. Thanks a lot and greetings from the future!