How to make material components work with Karma in unit testing Angular
Solution 1
All the providers are provided by calling forRoot()
on the module
imports: [ MaterialModule.forRoot() ]
For versions 2.0.0-beta.4
and later (since the forRoot
method has been removed):
imports: [ MaterialModule ]
For versions 2.0.0-beta.11
and later, since MaterialModule
has been removed, you have to import the modules you require for your test cases yourself:
imports: [ MatButtonModule, MatDialogModule ]
Solution 2
Current technique calls for individual imports of Angular Material modules, as MaterialModule
is deprecated and was removed in 2.0.0-beta.11:
import {
MatButtonModule,
MatIconModule
} from '@angular/material';
Then add the same list as imports in the TestBed config:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ... ],
imports: [
MatButtonModule,
MatIconModule,
...
],
providers: [ ... ]
})
.compileComponents();
}));
Solution 3
I've been struggling with this as well today and you need to mock out the necessary classes yourself by using providers in jasmine. It's a hassle and I wish there was a better way but at least no more errors...
If anyone has a better idea, please enlighten the rest of the community!
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AlertDialogComponent } from './alert-dialog.component';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
describe('AlertDialogComponent', () => {
let component: AlertDialogComponent;
let fixture: ComponentFixture<AlertDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatDialogModule],
declarations: [AlertDialogComponent],
providers: [
{
provide: MatDialogRef, useValue: {}
},
{
provide: MAT_DIALOG_DATA, useValue:{}
}
],
}).compileComponents();
}));
Related videos on Youtube
Snowman
Professional software developer. Science/history buff Cinephile/bookworm
Updated on May 28, 2020Comments
-
Snowman almost 4 years
I have an angular CLI project set up. I've made a form that uses angular material components, like
<md-card>
.I'm just starting out with writing my first Karma/Jasmine unit test, following the steps in the angular docs.
This is my component template:
<md-card [ngClass]="'dialog-card'"> <md-card-title [ngClass]="'dialog-title'"> {{title}} </md-card-title> <md-card-content> <form (ngSubmit)="login()" #loginForm="ngForm"> <md-input-container class="md-block"> <input md-input [(ngModel)]="user.email" name="userEmail" type="email" placeholder="Email" ngControl="userEmail" required> </md-input-container> <br> <md-input-container class="md-block"> <input md-input [(ngModel)]="user.password" name="userPassword" type="password" placeholder="Password" ngControl="userPassword" required> </md-input-container> <br> <tm-message msgText="Wrong username or password" *ngIf="showError"></tm-message> <br> <button md-button type="submit" [disabled]="!loginForm.form.valid">Login</button> <p (click)="openForgotPasswordModal()">Forgot Password?</p> </form> </md-card-content>
This is my karma spec:
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { MaterialModule, MdDialogRef, MdDialog } from '@angular/material'; import { FormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { TmLoginComponent } from './tm-login.component'; import { TmMessageComponent } from '../../shared/components/tm-message.component'; import { UserAuthenticationService } from '../login/user-authentication.service'; describe('TmLoginComponent (inline template)', () => { let comp: TmLoginComponent; let fixture: ComponentFixture < TmLoginComponent > ; let de: DebugElement; let el: HTMLElement; beforeEach(() => { TestBed.configureTestingModule({ declarations: [TmLoginComponent, TmMessageComponent], // declare the test component imports: [MaterialModule, FormsModule, RouterTestingModule.withRoutes( [{ path: 'login', component: TmLoginComponent }, ]) ], providers: [UserAuthenticationService], }); fixture = TestBed.createComponent(TmLoginComponent); comp = fixture.componentInstance; // TmLoginComponent test instance // query for the title <h1> by CSS element selector de = fixture.debugElement.query(By.css('.title')); el = de.nativeElement; }); it('should display original title', () => { fixture.detectChanges(); expect(el.textContent).toContain(comp.title); }); });
At this point, I'm just trying to run the basic unit test that the title is being displayed properly.
However, I'm getting a lot of material specific errors. Like
No provider for MdDialog.
I'm opening an md Dialog on clicking a link. THe code is in the (fairly long) .ts file, but that's not the issue here.
Where would I add MdDialog in the testbed? If I add it to providers, I get the error: "no provider for overlay". I don't know how to fix that.
Is there any way I can configure karma to include all material components at start?
Thanks.
-
Snowman over 7 yearsThanks for answering. I did that. Now I have
TypeError: Cannot read property 'nativeElement' of null
-
Paul Samsotha over 7 yearsI don't see any element (in your template) with a
title
class. I see adialog-title
class though -
Snowman over 7 yearsDo I need an element with a title class?
-
Paul Samsotha over 7 years
de = fixture.debugElement.query(By.css('.title'))
. Why do you think it (de
) is undefined. -
Snowman over 7 yearsSo sorry. I guess it regressed at some point. Still, I changed the class form
dialog-title
totitle
class and it still threw the same error. Then I created simple<p class='title'>{{title}}</p>
and it worked. I don't get it. Is it an issue withngClass
? -
Paul Samsotha over 7 yearsI don't, but why are you using
ngClass
anyway for just a simple string? Just useclass
. If you actually need to evaluate an expression, that's different -
Paul Samsotha over 7 yearsYou can always just print out the HTML content of the whole
fixture.nativeElemen.innerHTML
. Maybe that will show you what is actually being rendered (maybe give you a hint as to what's going on) -
Snowman over 7 yearsBTW, is there anything I could do in the karma conf file so I don't have to import material in each spec file?
-
Paul Samsotha over 7 yearsNot that I know of.