Mocking a parent FormGroup via @input in Jasmine

14,898

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();
  });
});
Share:
14,898
anonuser1234
Author by

anonuser1234

Updated on June 21, 2022

Comments

  • anonuser1234
    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 FormBuilder

    Cannot 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
    James Young over 5 years
    This is awesome. Thanks! Fyi missing a second closing parenthesis at the end of the 2nd beforeEach...
  • Pixxl
    Pixxl over 5 years
    @JamesYoung Thanks for letting me know, and glad this helped you! :)
  • anonuser1234
    anonuser1234 over 5 years
    Although 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
    Pixxl over 5 years
    Ah 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
    Till Kuhn over 3 years
    @Pixxl it just helped me. Thanks a lot and greetings from the future!