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: ("grantee" OR "grant" OR "sponsor" OR "contribute" OR "contributor") NEAR ("non-profit organization" OR "charities")">
</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?
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);
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With