'How to test that component template contains mat-error

I want to create test for component that checks that mat-error is displayed.

I've created a test but it's failing because DOM does not have mat-error at all during testing. Although it works fine when project is served.

Template snippet

<mat-error>
    Error message
</mat-error>

Test set up

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        MatFormFieldModule,
        ReactiveFormsModule,
        MatInputModule,
        MatFormFieldModule,
        BrowserAnimationsModule
      ],
      declarations: [MyComponent]
    }).compileComponents();
  }));

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

  fit('should display error', () => {
    const matErrorEl: HTMLElement = 
    fixture.debugElement.query(By.directive(MatError)).nativeElement;
    expect(matErrorEl).toBeTruthy();
  });
});


Solution 1:[1]

tldr; You have to touch the FormControl before any errors will show.


You have to first touch the component.
Using reactive forms (where nameFormControl is of type FormControl):

component.nameFormControl.markAsTouched();

Your mat-error element will now show in the view.


For a real scenario you will have an ngIf on the mat-error element and will need to set that error as well.

Example template:

<mat-error *ngIf="nameFormControl.hasError('required')">
    Error message
</mat-error>

Adding error:

component.nameFormControl.setErrors({ required: true} as ValidationErrors);

Similar issue for this question:
How to catch the <mat-error> error message text content using Protractor

Official docs on Angular Form Validation:
https://angular.io/guide/form-validation#why-check-dirty-and-touched

Solution 2:[2]

I followed an other way, I utilized a library named spectator for help me with scenarios using angular-material spectator

file test

import { createComponentFactory, Spectator } from '@ngneat/spectator';
describe('InputEmailComponent', () => {
 let spectator: Spectator<InputEmailComponent>;
 const createComponent = createComponentFactory({
   component: InputEmailComponent,
 });

 beforeEach(() => (spectator = createComponent()));

 it(`should get the component mat-error with a email empty`, () => {
   // Pass a value null for a email validator required
   spectator.component.email.setValue(null);

   expect(spectator.query('mat-error')).toHaveText('Insira um e-mail válido');
 });

});

file component

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

@Component({
  selector: "input-email",
  templateUrl: "./input-email.component.html",
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective,
    },
  ],
})
export class InputEmailComponent implements OnInit {
  @Input() style: string = "";
  email: FormControl;
  constructor() {
    this.email = new FormControl("", [Validators.required, Validators.email]);
  }
  ngOnInit(): void {}

  getErrorMessage(): string | null {
    if (this.email.hasError("required") || this.email.hasError("email")) {
      return "Insira um e-mail válido";
    }
    return null;
  }
}

file template html

<mat-form-field [style]="style" appearance="fill">
  <mat-label>e-mail</mat-label>
  <input
    type="email"
    formControlName="email"
    matInput
    placeholder="digite seu e-mail"
  />
  <mat-error *ngIf="!email.valid">{{ getErrorMessage() }}</mat-error>
</mat-form-field>

Solution 3:[3]

I was having the same issue the other day working with the angular testing-library and Jest. The answer of @RonanCodes actually brought me on the right track. I had to add the detectChanges() statement after the markAsTouched() as well, though.

Admittedly, my setup is a bit different, since the date picker is a separate shared component, where you can inject a map of errors to handle depending on the validator functions that you added to the form control.

Anyways, in any case that adding markAsTouched does not resolve your issue, try adding detectChanges as well.

it('should show error', async () => {
    const reqMsg = 'This field is required';
    const errors = new Map([['required', reqMsg]]);
    const control = new FormControl(null, [Validators.required]);
    const rc = await renderComponent('ariaLabel', 'label', control, errors);
    
    control.setErrors({ required: true });
    control.markAsTouched();
    rc.detectChanges();
    
    expect(screen.getByTestId('date-picker-error')).toHaveTextContent(reqMsg);
  });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 RonanCodes
Solution 2
Solution 3 Springer21