Andreas Van Greunen
Andreas Van Greunen

Reputation: 23

How to unit test validation message in Angular template driven forms

I have a very basic Angular template driven form with a single field which is required. A validation message is displayed if the field is invalid, which is the case when the component is first loaded as the field is required but empty. The code runs as expected when viewed in the application and the validation message is displayed.

When testing the component via a Jasmine unit test the validation message is not picked up and the test fails.

I am confident that the logic to look for the validation message is working, because if I remove the *ngIf directive on the message DIV then the test passes.

I have tried the following:

Template:

<form #form="ngForm">

  <label>First name:</label>

  <input #firstName="ngModel"
    type="text"
    name="firstName"
    [(ngModel)]="firstNameText"
    required />

  <div class="validation-error" *ngIf="firstName.invalid">
      Please enter a valid first name
  </div>
</form>

Component class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-person-form',
  templateUrl: './person-form.component.html'
})
export class PersonFormComponent  {
  public firstNameText: string;
}

Jasmine test spec:

import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
import { PersonFormComponent } from './person-form.component';
import { FormsModule } from '@angular/forms';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ FormsModule ],
      declarations: [ PersonFormComponent ]
    });

    fixture = TestBed.createComponent(PersonFormComponent);
    fixture.detectChanges();
  });

  it('should show a validation error if the first name was touched but left empty', () => {
    let firstNameValidationError: DebugElement;

    // try to get a handle to the validation message (should exist as form is invalid):
    firstNameValidationError = fixture.debugElement.query(By.css('.validation-error'));

    // the validation error should be found:
    expect(firstNameValidationError).toBeTruthy();
  });
});

Upvotes: 1

Views: 5670

Answers (3)

Andreas Van Greunen
Andreas Van Greunen

Reputation: 23

The complete revised test is as follows, thanks to Amit Chigadani for solving this:

import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
import { PersonFormComponent } from './person-form.component';
import { FormsModule } from '@angular/forms';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';

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

  beforeEach(async() => {
    TestBed.configureTestingModule({
      imports: [ FormsModule ],
      declarations: [ PersonFormComponent ]
    });

    fixture = TestBed.createComponent(PersonFormComponent);
    fixture.detectChanges();
  });

  it('should show a validation error if the first name was touched but left empty', () => {
    let firstNameValidationError: DebugElement;
    fixture.detectChanges(); // run change detection

    // try to get a handle to the validation message (should exist as form is invalid):
    firstNameValidationError = fixture.debugElement.query(By.css('.validation-error'));

    // the validation error should be found:
    expect(firstNameValidationError).toBeTruthy();
  });
});

Upvotes: 1

Amit Chigadani
Amit Chigadani

Reputation: 29705

Component initialization should always be done in a async block

beforeEach(async() => {
    TestBed.configureTestingModule({
      imports: [ FormsModule ],
      declarations: [ PersonFormComponent ]
    });

    fixture = TestBed.createComponent(PersonFormComponent);
    fixture.detectChanges();
});

Also you may need to run change detection once again after the component is initialised

it('should show a validation error if the first name was touched but left empty', () => {
    let firstNameValidationError: DebugElement;

    fixture.detectChanges(); // run change detection
    firstNameValidationError = fixture.debugElement.query(By.css('.validation-error'));

    // the validation error should be found:
    expect(firstNameValidationError).toBeTruthy();
});

Upvotes: 1

dileepkumar jami
dileepkumar jami

Reputation: 2265

Add these lines at the beginning of the test

 let component = fixture.componentInstance;
    component.firstNameText = '';
    fixture.detectChanges();

and after this, set some name to the firstNameText as below

component.firstNameText = 'test';

At this point, you should expect the error to be falsy.

Upvotes: 0

Related Questions