Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test FormGroupDirective in component?

I've some problems to tests component with FormGroupDirective in viewProviders. Can't create mock of parent and set empty formGroup.

My component:

@Component({
   (...)
   viewProviders: [
      {
        provide: ControlContainer, useExisting: FormGroupDirective
      }
    ]
  })
export class SomeNestedFormComponent implements OnInit {
  form: FormGroup;

  constructor(private fb: FormBuilder, private parent: FormGroupDirective) {}

  ngOnInit() {
    this.form = this.parent.form;
    this.form.addControl('field', this.createSomeFormGroup());
  }
}

Spec:

describe('SomeNestedFormComponent', () => {
  let component: SomeNestedFormComponent;
  let fixture: ComponentFixture<SomeNestedFormComponent>;
  let formGroupDirective: Partial<FormGroupDirective>;

  beforeEach(async(() => {
    formGroupDirective = {
      form: new FormGroup({})
    };

    TestBed.configureTestingModule({
      imports: [
        SharedModule,
        FormsModule,
        ReactiveFormsModule
      ],
      declarations: [SomeNestedFormComponent],
      providers: []
    })
      .overrideComponent(PermissionListComponent, {
        set: {
          viewProviders: [
            {
              provide: FormGroupDirective, useValue: formGroupDirective
            }
          ]
        }
      })
      .compileComponents()
      .then(() => {
        fixture = TestBed.createComponent(SomeNestedFormComponent);
        component = fixture.componentInstance;
        component.ngOnInit();
        fixture.detectChanges();
      });
  }));

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

This throw: Error: formGroupName must be used with a parent formGroup directive. (...) Try to handle FormGroupDirective as a service with spyOn, but it throws TypeError: this.form.addControl is not a function

describe('SomeNestedFormComponent', () => {
  (...)
  let fgdSpy:  jasmine.SpyObj<SomeNestedFormComponent>;

  beforeEach(async(() => {
    const FGDirectiveSpy = jasmine.createSpyObj('FormGroupDirective', ['form', 'addControl']);

    TestBed.configureTestingModule({
      (...)
      providers: [
        {provide: FormGroupDirective, useValue: FGDirectiveSpy}
      ]
    })
      .compileComponents()
      .then(() => {
        fgdSpy = TestBed.get(FormGroupDirective);
        (...)
      });

Is there any way to test that component?

like image 333
Blazej456 Avatar asked Nov 22 '18 08:11

Blazej456


2 Answers

Let me share what i did on this scene.

Created mockFormGroup as like in my parent component and then created mock FormControlDirective as formGroupDirective to be used in useValue providers.

Finally assign parent component's form to mocked formGroup like below

component.parent.form = mockFormGroup;

It is essential to add FormControlDirective in providers to avoid the error No provider for FormControlDirective.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { ReactiveFormsModule, FormGroupDirective, FormControlDirective, FormBuilder, ControlContainer, FormGroup, FormControl } from '@angular/forms';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let formGroupDirective: FormControlDirective;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ MyComponent ],
      imports: [
        HttpClientTestingModule,
        ReactiveFormsModule
      ],
      providers: [ FormGroupDirective,
        { provide: ControlContainer, useValue: formGroupDirective }
      ],
      schemas: [NO_ERRORS_SCHEMA]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;

    //mock parent formGroup
    const mockFormGroup: FormGroup = new FormGroup({
    });

    //dummy formgroupDirective to avoid undefined addControl function
    const formGroupDirective: FormGroupDirective = new FormGroupDirective([], []);
    
    component.parent.form = mockFormGroup;
    component.ngOnInit();
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});
like image 167
HelloWorld Avatar answered Sep 18 '22 14:09

HelloWorld


Here is how I managed to fix the problem of injecting a FormGroupDirective.

My componet -

import {Component, Input, OnInit} from '@angular/core';
import {ControlContainer, FormControl, FormGroupDirective} from "@angular/forms";

@Component({
  selector: 'app-checkbox',
  templateUrl: './checkbox.component.html',
  styleUrls: ['./checkbox.component.scss'],
  viewProviders: [{provide: ControlContainer, useExisting: FormGroupDirective}]
})
export class CheckboxComponent implements OnInit {

  @Input() controlName: string;
  public formControl: FormControl;

  constructor(private formGroupDirective: FormGroupDirective) {

  }

  ngOnInit(): void {
    this.formControl = this.formGroupDirective.form.get(this.controlName) as FormControl;
  }

}

The test -

import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {CheckboxComponent} from './checkbox.component';
import {FormBuilder, FormGroupDirective, ReactiveFormsModule} from "@angular/forms";

describe('CheckboxComponent', () => {
  let component: CheckboxComponent;
  let fixture: ComponentFixture<CheckboxComponent>;

  beforeEach(waitForAsync(() => {
    const fb = new FormBuilder()

    const formGroupDirective = new FormGroupDirective([], []);
    formGroupDirective.form = fb.group({
      test: fb.control(null)
    });

    TestBed.configureTestingModule({
      declarations: [CheckboxComponent],
      imports: [
        ReactiveFormsModule
      ],
      providers: [
        FormGroupDirective,
        FormBuilder,
        {provide: FormGroupDirective, useValue: formGroupDirective}
      ]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(CheckboxComponent);
    component = fixture.componentInstance;
    component.controlName = 'test';

    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
like image 32
DaveLV2 Avatar answered Sep 21 '22 14:09

DaveLV2