Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why ngForm "controls" property is empty in my test? (Angular)

I have a component which employs template-driven form

<form (ngSubmit)="onSearchCorpus(form)" #form="ngForm">
<combo-box [(ngModel)]="model.corpus" name="corpus" #corpus="ngModel"></combo-box>
<input [(ngModel)]="model.label" name="label" id="label" type="text" required pattern="^(?!\s*$)[\w\_\-\:\s]*" maxlength="50" class="form-control label-name" autocomplete="off" #label="ngModel">
<textarea [(ngModel)]="model.query" name="query" id="query" maxlength="3600" required validateQuerySyntax class="form-control search-query" #query="ngModel" #queryControl
        placeholder="Example: (&quot;grantee&quot; OR &quot;grant&quot; OR &quot;sponsor&quot; OR &quot;contribute&quot; OR &quot;contributor&quot;) NEAR (&quot;non-profit organization&quot; OR &quot;charities&quot;)">
      </textarea>
 <button [disabled]="corpusValidationInProgress" type="submit" class="button-level-one">Search</button>
</form>

In the method that handles form submission I access controls property of NgForm instance and it works fine in browser.

onSearchCorpus(formData: NgForm) {
   ...
   const corpusErrors = formData.controls.corpus.errors;
   ...
}

However when I try to test this method with Karma the NgForm's controls property is empty. I'm confused why is that. The method fails with error cannot read property "errors" of undefined.

Here is how my test looks like:

it('should not perform corpusSearch if selected corpus no longer exists', () => {
    component.ngOnInit();
    const form = fixture.debugElement.query(By.css('form'));
    form.triggerEventHandler('submit', null);
    ...
});

and this is how I'm setting up my test suit:

beforeEach(async(() => {
    TestBed.configureTestingModule({
        // schemas: [NO_ERRORS_SCHEMA],
        imports: [
            FormsModule,
            PopoverModule
        ],
        providers: [
            CorpusSearchService,
            { provide: ApiService, useValue: ApiServiceStub },
            { provide: Router, useClass: RouterStab },
        ],
        declarations: [
            SearchCorpusComponent, //<--component under test
            ComboBoxComponent //<-- 3rd party combobox which is used for first control 
        ]
    }).compileComponents();
}));

beforeEach(() => {
    fixture = TestBed.createComponent(SearchCorpusComponent);
    component = fixture.componentInstance;
    apiService = fixture.debugElement.injector.get(ApiService);
    subject = new Subject();
});

So, why controls is empty in test environment?

like image 357
dKab Avatar asked Oct 16 '17 12:10

dKab


1 Answers

The reason why controls is "empty" is that the controls were not initialized "yet" in the test environment at the time of calling onSearchCorpus.

I've prepared a Plunkr (see form.component.spec file) which demonstrates this issue. In the same file there is also a working solution.

So in short, in order to make this work you need to use fakeAsync mechanism.

So instead of:

  it('testing form the wrong way', () => {

    fixture.detectChanges();

    comp.onSubmit(); // Validates and submits a form

    expect(comp.submitted).toEqual(false);
  });

you should write a test like:

  it('testing form the proper way', fakeAsync(() => {

    // This first detectChanges is necessary to properly set up the form
    fixture.detectChanges();

    // Tick needs to be called in order for form controls to be registered properly.
    tick();

    comp.onSubmit(); // Validates and submits a form

    expect(comp.submitted).toEqual(false);

  }));
like image 182
PrimosK Avatar answered Oct 19 '22 20:10

PrimosK