How to mock AngularFire 2 service in unit test? - unit-testing

I'm trying to set up unit tests for a sample Angular 2 app using AngularFire 2 auth, the component is fairly simple:
import { Component } from '#angular/core';
import { AngularFire, AuthProviders } from 'angularfire2';
#Component({
moduleId: module.id,
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
isLoggedIn: boolean;
constructor(public af: AngularFire) {
this.af.auth.subscribe(auth => {
if (auth) {
this.isLoggedIn = true;
} else {
this.isLoggedIn = false;
}
});
}
loginWithFacebook() {
this.af.auth.login({
provider: AuthProviders.Facebook
});
}
logout() {
this.af.auth.logout();
}
}
All I'm doing is wrapping around the login and logout methods in AngularFire so I was thinking about using a mock to check if the methods were called but I'm not sure where to start, I tried doing the following in my spec file:
import { provide } from '#angular/core';
import { AngularFire } from 'angularfire2';
import {
beforeEach, beforeEachProviders,
describe, xdescribe,
expect, it, xit,
async, inject
} from '#angular/core/testing';
import { AppComponent } from './app.component';
spyOn(AngularFire, 'auth');
beforeEachProviders(() => [
AppComponent,
AngularFire
]);
describe('App Component', () => {
it('should create the app',
inject([AppComponent], (app: AppComponent) => {
expect(app).toBeTruthy();
})
);
it('should log user in',
inject([AppComponent], (app: AppComponent) => {
expect(app.fb.auth.login).toHaveBeenCalled();
})
);
it('should log user out',
inject([AppComponent], (app: AppComponent) => {
expect(app.fb.auth.logout).toHaveBeenCalled();
})
);
});
However I'm not sure how to mock the login and logout methods since they're part of the auth property, is there a way to mock auth and also the returning login and logout methods?

In this snippet:
beforeEach(() => addProviders([
AppComponent,
AngularFire
]);
You set (or override) the providers that will be used in your test.
That being said, you can create a different class, a mock if you will, and, using the { provide: originalClass, useClass: fakeClass } notation, provide it instead of the AngularFire actual class.
Something like this:
class AngularFireAuthMock extends AngularFireAuth { // added this class
public login() { ... }
public logout() { ... }
}
class AngularFireMock extends AngularFire { // added this class
public auth: AngularFireAuthMock;
}
beforeEach(() => addProviders([
AppComponent,
{ provide: AngularFire, useClass: AngularFireMock } // changed this line
]);
And the AngularFires in your tests will be AngularFireMocks.

hope it is not off the topic, but the easiest solution I have found how to mock the FirebaseDatabase.
var object = function() {
var obj = { valueChanges() {
return of({data:'data'});
}
}
return obj;
}
providers: [..., { provide : AngularFireDatabase,
useValue: {object : object }} ]
instead of data:'data' you can mock whatever data you need. The functions can be modified as you wish.

Similar to #jan, I made a mock using some utility functions:
import {AngularFireAuth} from '#angular/fire/auth';
import {AngularFireDatabase} from '#angular/fire/database';
import {auth} from 'firebase/app';
import { Observable, of, Subscription } from 'rxjs';
/**
* Mocks the Firebase auth by automatically logging in.
*/
export const AngularFireAuthMock = jasmine.createSpy('signInWithEmailAndPassword')
.and.returnValue(Promise.resolve({uid: 'fakeuser'}));
/**
* Mocks an AngularFireDatabase that always returns the given data for any path.
*/
export function mockAngularFireDatabase(data): AngularFireDatabase {
return {
object: (path: string): any => {
return {
valueChanges() {
return of(data);
}
}
}
} as AngularFireDatabase;
}
and then you can use them in your spec like this:
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TweakComponent ],
imports: [ MatDialogModule, RouterTestingModule ],
providers: [
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: AngularFireDatabase, useValue: mockAngularFireDatabase({testdata:'hi'})},
{ provide: AngularFireAuth, useValue: AngularFireAuthMock}
],
})
.compileComponents();
});

Related

How to correctly test Angular4 application with Auth0 integration?

I am currently working on an Angular4 web application which uses Auth0 for authentication.
While the authentication works as expected the integration of Auth0 has broken (lets fail) the default tests (Karma unit tests) of my application.
My code looks as follows:
// app.component.ts
/*
* Angular 2 decorators and services
*/
import {
Component,
ViewEncapsulation
} from '#angular/core';
import { Auth } from './auth.service';
/*
* App Component
* Top Level Component
*/
#Component({
selector: 'app',
providers: [ Auth ],
encapsulation: ViewEncapsulation.None,
styleUrls: [
'./app.component.scss'
],
template: `
<div class="container-fluid">
<router-outlet></router-outlet>
</div>
`
})
export class AppComponent {
public angularclassLogo = 'assets/img/ExampleApp_smallLogo.png';
public name = 'ExampleApp';
public url = 'https://www.example.com/';
constructor(private auth: Auth) {
this.auth.handleAuth();
}
}
// auth.service.ts
import { Injectable } from '#angular/core';
import { tokenNotExpired } from 'angular2-jwt';
import { Router } from '#angular/router';
import { Http, Headers, RequestOptions, RequestMethod, Response } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/filter';
import Auth0Lock from 'auth0-lock';
import Auth0 from 'auth0-js';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { myConfig, postConfig, necessaryRoles } from './auth.config';
// Avoid name not found warnings
// declare var auth0: any;
#Injectable()
export class Auth {
public lock = new Auth0Lock(myConfig.clientID, myConfig.domain, myConfig.lock);
public userProfile: any;
public idToken: string;
public signUpIncomplete: boolean;
// Configure Auth0
private auth0 = new Auth0.WebAuth({
domain: myConfig.domain,
clientID: myConfig.clientID,
redirectUri: myConfig.redirectUri,
responseType: myConfig.responseType
});
// Create a stream of logged in status to communicate throughout app
private loggedIn: boolean;
private loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);
constructor(private router: Router, private http: Http) {
// Set userProfile attribute of already saved profile
this.userProfile = JSON.parse(localStorage.getItem('profile'));
}
...
}
// app.component.spec
import { NO_ERRORS_SCHEMA } from '#angular/core';
import {
async,
TestBed,
ComponentFixture
} from '#angular/core/testing';
import {
BaseRequestOptions,
HttpModule,
Http,
XHRBackend,
} from '#angular/http';
import { RouterTestingModule } from '#angular/router/testing';
import { MockBackend } from '#angular/http/testing';
// Load the implementations that should be tested
import { AppComponent } from './app.component';
import { AppState } from './app.service';
import { Auth } from './auth.service';
describe(`App`, () => {
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
// async beforeEach
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent ],
imports: [RouterTestingModule, HttpModule],
schemas: [NO_ERRORS_SCHEMA],
providers: [
AppState,
Auth,
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory:
(backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
]
})
.compileComponents(); // compile template and css
}));
// synchronous beforeEach
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding
});
it(`should be readly initialized`, () => {
expect(fixture).toBeDefined();
expect(comp).toBeDefined();
});
it(`should be ExampleApp`, () => {
expect(comp.url).toEqual('https://www.example.com/');
expect(comp.angularclassLogo).toEqual('assets/img/ExampleApp_smallLogo.png');
expect(comp.name).toEqual('ExampleApp');
});
});
The problem is that both App: should be readly initialized and App: should be MyApp fail with Cannot read property 'WebAuth' of undefined although WebAuth is defined in auth.service.ts which is then imported in app.component.spec.
Am I missing any import or declaration?
I finally solved the questions by myself.
I had to create a mock for the Auth service.
Further, I had to override the App component so that it uses that mock object instead of the real Auth service.
Therefore the solution looks as follows:
import { NO_ERRORS_SCHEMA } from '#angular/core';
import {
async,
TestBed,
ComponentFixture
} from '#angular/core/testing';
import {
BaseRequestOptions,
HttpModule,
Http,
XHRBackend,
} from '#angular/http';
import { RouterTestingModule } from '#angular/router/testing';
import { MockBackend } from '#angular/http/testing';
// Load the implementations that should be tested
import { AppComponent } from './app.component';
import { Auth } from './auth.service';
// Mock our Auth service
export class MockAuthService {
public handleAuth(): void {
return;
}
}
describe(`App`, () => {
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
// async beforeEach
beforeEach(async(() => {
TestBed
.configureTestingModule({
declarations: [ AppComponent ],
imports: [RouterTestingModule, HttpModule],
schemas: [NO_ERRORS_SCHEMA],
providers: [
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
]
})
.overrideComponent(AppComponent, {
set: {
providers: [{ provide: Auth, useValue: new MockAuthService() }]
}
})
.compileComponents();
}));
// synchronous beforeEach
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding
});
it(`should be readly initialized`, () => {
expect(fixture).toBeDefined();
expect(comp).toBeDefined();
});
it(`should be ExampleApp`, () => {
expect(comp.url).toEqual('https://www.example.com/');
expect(comp.angularclassLogo).toEqual('assets/img/ExampleApp_smallLogo.png');
expect(comp.name).toEqual('ExampleApp');
});
});

Unit testing and mocking a service with DI

I have been struggling with this for a while, and I'm hoping someone can help. I have a component that uses a service to get data. I am attempting to add unit tests to it. My problem is that the tests always fail with "Error: No provider for Http". Here is my code:
Service:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import { Contact } from './contact.model';
#Injectable()
export class ContactsService {
constructor(private http: Http) { }
public getContacts(): Observable<Array<Contact>> {
return this.http.get('assets/contacts.json').map(res => {
let r = res.json().Contacts;
return r;
});
}
}
Component:
import { Component, OnInit, NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { Contact } from '../contact.model';
import { ContactsService } from '../contacts.service';
#Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.css'],
providers: [ContactsService]
})
export class ContactsComponent implements OnInit {
contactsAll: Array<Contact>;
contacts: Array<Contact>;
constructor(private contactsService: ContactsService) { }
ngOnInit() {
this.contactsService.getContacts().subscribe((x) => {
this.contactsAll = x;
this.contacts = this.contactsAll;
});
}
}
Tests:
import { async, ComponentFixture, TestBed, inject } from '#angular/core/testing';
import { FormsModule } from '#angular/forms';
import { By } from '#angular/platform-browser';
import { Observable } from 'rxjs/Rx';
import { ContactsComponent } from './contacts.component';
import { ContactsService } from '../contacts.service';
import { Contact } from '../contact.model';
class MockContactsService extends ContactsService {
constructor() {
super(null);
}
testContacts: Array<Contact> = [
new Contact("test1 mock", 12345, 10000),
new Contact("test2 mock", 23456, 20000)
];
public getContacts(): Observable<Array<Contact>> {
return Observable.of(this.testContacts);
}
}
describe('ContactsComponent', () => {
let component: ContactsComponent;
let fixture: ComponentFixture<ContactsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ContactsComponent],
// providers: [{ provide: ContactsService, useClass: MockContactsService }] // this is needed for the service mock
}).overrideComponent(ContactsService, {// The following is to override the provider in the #Component(...) metadata
set: {
providers: [
{ provide: ContactsService, useClass: MockContactsService },
]
}
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContactsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('1st test', () => {
it('true is true', () => expect(true).toBe(true));
})
});
Let's try this:
First, move your providers array from your component to your NgModule. It's better to provide your services at the module level since it de-couples your providers from your component tree structure (unless you specifically want to have a separate instance of a provider per component, and from your simplified use case, there's no need for a separate instance per component).
so,
#Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.css'],
/// providers: [ContactsService] <-- remove this line
})
export class ContactsComponent implements OnInit {
.....
and add it to the NgModule that declares your ContactsComponent
#NgModule({
imports: ..
declarations: ...
providers: [ContactsService] // <-- provider definition moved to here
})
export class ModuleDeclaringContactsComponent
Once you do that, then mocking the ContactsService in your test is easy to implement.
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ContactsComponent],
providers: [{ provide: ContactsService, useClass: MockContactsService }] // this is needed for the service mock
});
With that, you should be good to go.
Sorry everyone - turns out it was something completely different.
I modified my code as per snorkpete's answer, and I am going to mark that as the answer, as I believe that is the cleanest approach.
The real problem came from using Angular Cli to create my project. It automatically created tests for my component and my service. This meant the code in the service test was causing the error, not the code in the component. I commented out the code in the service test and everything passed.
Annoyingly, there was no indication in any of the failures that this is where the error was coming from!
In case if component should have Service in providers we can ovverride metadata by TestBed.overrideComponent():
#Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.css'],
providers: [ContactsService] // IF YOU NEED IT
})
we need to do next:
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [ SomeComponent ],
providers: [
{provide: SomeService, useValue: SomeMock},
provideMockStore({system: {userConfig: {viewSettings: {theme: ThemeName.light}}}} as any)
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
TestBed.overrideComponent(SomeComponent , { set: { providers: [{provide: SomeService, useValue: SomeMock}]}})
}));
beforeEach(() => {
fixture = TestBed.createComponent(SomeComponent );
component = fixture.componentInstance;
fixture.detectChanges();
});
more info here https://codecraft.tv/courses/angular/unit-testing/dependency-injection/

Angular2 DI error in jasmine test

I'm writing some angular2 unit tests using jasmine and working on a unit test for a component. For some reason, when I try to instantiate the component using the TestBed class, I get a dependency injection error.
LoginComponent:
import { Component } from '#angular/core';
import { LoginService } from './login.service';
#Component({
selector: 'login',
template: require('./login.template.pug'),
styles: [require('./style.scss')],
providers: [LoginService]
})
export class LoginComponent {
public username: string;
public password: string;
constructor(private loginService: LoginService) {
}
public login(username, password) {
this.loginService.login(username, password)
.then((token: string) => {
})
}
}
LoginComponentTest:
import { TestBed, inject, async, ComponentFixture } from '#angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { LoginComponent } from './login.component';
import { LoginService } from './login.service';
import { BrowserModule } from '#angular/platform-browser';
describe('LoginComponent', () => {
class MockClass {
get(url): Promise<any> {
throw Error('not implemented');
};
}
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
LoginComponent
],
providers: [
LoginComponent,
{ provide: LoginService, useClass: MockClass },
],
imports: [
FormsModule,
BrowserModule,
ReactiveFormsModule
]
})
}));
it('will let user login', async(() => {
TestBed.compileComponents()
.then(() => {
let fixture: ComponentFixture<LoginComponent> = TestBed.createComponent(LoginComponent);
});
}));
});
The line
let fixture: ComponentFixture<LoginComponent> = TestBed.createComponent(LoginComponent);
gives the following error:
Chrome 57.0.2987 (Mac OS X 10.12.3) LoginComponent will let user login
FAILED Error: DI Error
at NoProviderError.ZoneAwareError (webpack:///~/zone.js/dist/zone.js:958:0 <-
config/spec-bundle.js:75553:33)
I have the LoginComponent declared along with its own dependency, LoginService.
Any ideas about what's going on?
Okay, I figured it out, kind of. Apparently the issue has to do with the providers declaration in LoginComponent:
providers: [LoginService]
Once I moved that out of LoginComponent and into AppModule, things started working. Would be interested to know why the test doesn't work with it declared inside the component itself.

Unit testing Angular 2 authGuard; spy method is not being called

I'm trying to unit test my auth guard service. From this answer I was able to get this far, but now when I run the unit test for this, it says Expected spy navigate to have been called.
How to I get my spied router to be used as this.router in the service?
auth-guard.service.ts
import { Injectable } from '#angular/core';
import { Router, CanActivate } from '#angular/router';
#Injectable()
export class AuthGuardService {
constructor(private router:Router) { }
public canActivate() {
const authToken = localStorage.getItem('auth-token');
const tokenExp = localStorage.getItem('auth-token-exp');
const hasAuth = (authToken && tokenExp);
if(hasAuth && Date.now() < +tokenExp){
return true;
}
this.router.navigate(['/login']);
return false;
}
}
auth-guard.service.spec.ts
import { TestBed, async, inject } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { AuthGuardService } from './auth-guard.service';
describe('AuthGuardService', () => {
let service:AuthGuardService = null;
let router = {
navigate: jasmine.createSpy('navigate')
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
AuthGuardService,
{provide:RouterTestingModule, useValue:router}
],
imports: [RouterTestingModule]
});
});
beforeEach(inject([AuthGuardService], (agService:AuthGuardService) => {
service = agService;
}));
it('checks if a user is valid', () => {
expect(service.canActivate()).toBeFalsy();
expect(router.navigate).toHaveBeenCalled();
});
});
Replacing RouterTestingModule with Router like in the example answer throws Unexpected value 'undefined' imported by the module 'DynamicTestModule'.
Instead of stubbing Router, use dependency injection and spy on the router.navigate() method:
import { TestBed, async, inject } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { Router } from '#angular/router';
import { AuthGuardService } from './auth-guard.service';
describe('AuthGuardService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthGuardService],
imports: [RouterTestingModule]
});
});
it('checks if a user is valid',
// inject your guard service AND Router
async(inject([AuthGuardService, Router], (auth, router) => {
// add a spy
spyOn(router, 'navigate');
expect(auth.canActivate()).toBeFalsy();
expect(router.navigate).toHaveBeenCalled();
})
));
});
https://plnkr.co/edit/GNjeJSQJkoelIa9AqqPp?p=preview
For this test, you can use the ReflectiveInjector to resolve and create your auth-gaurd service object with dependencies.
But instead of passing the actual Router dependency, provide your own Router class (RouterStub) that has a navigate function. Then spy on the injected Stub to check if navigate was called.
import {AuthGuardService} from './auth-guard.service';
import {ReflectiveInjector} from '#angular/core';
import {Router} from '#angular/router';
describe('AuthGuardService', () => {
let service;
let router;
beforeEach(() => {
let injector = ReflectiveInjector.resolveAndCreate([
AuthGuardService,
{provide: Router, useClass: RouterStub}
]);
service = injector.get(AuthGuardService);
router = injector.get(Router);
});
it('checks if a user is valid', () => {
let spyNavigation = spyOn(router, 'navigate');
expect(service.canActivate()).toBeFalsy();
expect(spyNavigation).toHaveBeenCalled();
expect(spyNavigation).toHaveBeenCalledWith(['/login']);
});
});
class RouterStub {
navigate(routes: string[]) {
//do nothing
}
}

"Can't resolve all parameters for MdDialogRef: (?)" Error when testing NG2 Material Dialog component

I have a login component as follows:
import { Component, OnInit } from '#angular/core';
import { MdDialogRef } from '#angular/material';
import { AuthService } from '../../core/services/auth.service';
#Component({
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginDialogComponent implements OnInit {
model: {
email: string,
password: string
};
error;
constructor(
private authService: AuthService,
private dialogRef: MdDialogRef<LoginDialogComponent>
) { }
ngOnInit() {
this.model = {
email: '',
password: ''
};
}
signin() {
this.error = null;
this.authService.login(this.model.email, this.model.password).subscribe(data => {
this.dialogRef.close(data);
}, err => {
this.error = err.json();
});
}
}
And I have a test spec for this component as follows:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { MdDialogRef, OverlayRef } from '#angular/material';
import { AuthService } from '../../core/services/auth.service';
import { LoginDialogComponent } from './login.component';
describe('Component: Login', () => {
let component: LoginDialogComponent;
let fixture: ComponentFixture<LoginDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
LoginDialogComponent
],
imports: [],
providers: [
AuthService,
MdDialogRef,
OverlayRef
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I've tried a million different things, and no matter what I do, I get the following error:
Can't resolve all parameters for MdDialogRef: (?)
Here's the code for MdDialogRef, which only has 1 parameter, OverlayRef. What am I missing?
import { OverlayRef } from '../core';
import { Observable } from 'rxjs/Observable';
/**
* Reference to a dialog opened via the MdDialog service.
*/
export declare class MdDialogRef<T> {
private _overlayRef;
/** The instance of component opened into the dialog. */
componentInstance: T;
/** Subject for notifying the user that the dialog has finished closing. */
private _afterClosed;
constructor(_overlayRef: OverlayRef);
/**
* Close the dialog.
* #param dialogResult Optional result to return to the dialog opener.
*/
close(dialogResult?: any): void;
/** Gets an observable that is notified when the dialog is finished closing. */
afterClosed(): Observable<any>;
}
EDIT: taking a clue from #Ryan's comment, I tried removing the MdDialogRef provider entirely and got the following error:
Can't resolve all parameters for OverlayRef: (?, ?, ?)
This leads me to believe that the problem is actually w/MdDialogRef trying to resolve OverlayRef, not w/MdDialogRef itself.
WORKING EXAMPLE The code below is the actual working code, per Yurzui's suggestion.
/* tslint:disable:no-unused-variable */
import { NgModule } from '#angular/core';
import { async, TestBed } from '#angular/core/testing';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { MaterialModule, MdDialogModule, MdToolbarModule, MdDialog, MdDialogRef } from '#angular/material';
import { CoreModule } from '../../core/core.module';
import { LoginDialogComponent } from './login.component';
#NgModule({
declarations: [
LoginDialogComponent
],
entryComponents: [
LoginDialogComponent
],
exports: [
LoginDialogComponent
],
imports: [
CommonModule,
CoreModule,
FormsModule,
MaterialModule.forRoot(),
MdDialogModule.forRoot(),
MdToolbarModule.forRoot()
]
})
class LoginDialogSpecModule { }
describe('Component: Login Dialog', () => {
let component: LoginDialogComponent;
let dialog: MdDialog;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
LoginDialogSpecModule
]
});
});
beforeEach(() => {
dialog = TestBed.get(MdDialog);
let dialogRef = dialog.open(LoginDialogComponent);
component = dialogRef.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
There is an issue ComponentFactoryResolver is not aware of components compiled via TestBed
According to this problem angular2 team offers workaround by creating real module with entryComponents property
https://github.com/angular/material2/blob/2.0.0-beta.1/src/lib/dialog/dialog.spec.ts#L387-L402
So your test could write like this:
import { MdDialog, MdDialogModule } from '#angular/material';
#NgModule({
declarations: [TestComponent],
entryComponents: [TestComponent],
exports: [TestComponent],
})
class TestModule { }
describe('Component: Login', () => {
let component: TestComponent;
let dialog: MdDialog;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [TestModule, MdDialogModule]
});
});
beforeEach(() => {
dialog = TestBed.get(MdDialog);
let dialogRef = dialog.open(TestComponent);
component = dialogRef.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Plunker Example
I was getting the same error when running my code normally, i.e. I did not write a test case.
I found that the line provides: [ MdDialogRef ]in my main component was giving this exact same error, and everything worked without it.