Mocking a parent FormGroup via @input in Jasmine
Asked Answered
B

1

15

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

Buxtehude answered 7/3, 2018 at 22:39 Comment(2)
Are you able to resolve this problem? My situation is also same. But I am getting "Can't resolve all parameters for FormGroup: (?, ?, ?)" error.Mikimikihisa
I noticed a typo in your code: inject([FormBuidler], (fb: FormBuilder) should be: inject([FormBuilder], (fb: FormBuilder)Pupiparous
P
23

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();
  });
});
Pupiparous answered 18/7, 2018 at 2:49 Comment(5)
This is awesome. Thanks! Fyi missing a second closing parenthesis at the end of the 2nd beforeEach...Brebner
@JamesYoung Thanks for letting me know, and glad this helped you! :)Pupiparous
Although I am no longer doing this type of coding I appreciated your answer! Wish I found this back when I had the problem hahaBuxtehude
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.Pupiparous
@Pupiparous it just helped me. Thanks a lot and greetings from the future!Katusha

© 2022 - 2024 — McMap. All rights reserved.