Unit Tests with Angularfire2 on angular-cli - unit-testing

Created a new angular 2 project with angular-cli
Below is the default component app.component.ts and it has app.component.spec.ts
import { Component } from '#angular/core';
import { AngularFire } from 'angularfire2'; // import angularfire2
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
af: AngularFire;
constructor(af: AngularFire){
this.af=af;
this.firebaseCall();
}
//push data to firebase collection
firebaseCall(){
let post=this.af.database.list('/post');
post.push({a:'test'});
}
}
To Implement Unit test for the above firebaseCall() in app.component.spec.ts
I have added/updated below lines in app.component.spec.ts
import { AngularFire } from 'angularfire2';
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,AngularFire // extra added
],
});
TestBed.compileComponents();
});
I get below error while ng test
Unexpected value 'AngularFire' declared by the module 'DynamicTestModule'

You need to mock the services in the test.
Here you are injecting AngularFire. So you test setup should be like this.
import { AngularFire } from 'angularfire2';
...
const mockFirebase = jasmine.createSpyObj('af',['database']);
af.database.and.returnValue({list: Rx.Observable.of([])});
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
providers:[
{provide:AngularFire, useValue:}]
});
TestBed.compileComponents();
});
Hope, this helps you. always mock your providers in the unit-test and don't make a http call.

Related

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/

Testing a component with dependencies using karma & jasmine

I'm new to angular 2 and I have some problems testing my code. I use the jasmine testing framework and the karma test runner to test my app.
I have a component (called GroupDetailsComponent) that I want to test. This component uses two services (GroupService & TagelerServie, both have CRUD methods to talk to an API) and some pipes in the html file. My component looks like this:
import 'rxjs/add/operator/switchMap';
import { Component, Input, OnInit } from '#angular/core';
import { Tageler } from '../../tagelers/tageler';
import { TagelerService } from '../../tagelers/tageler.service';
import { Params, ActivatedRoute } from '#angular/router';
import { GroupService} from "../group.service";
import { Group } from '../group';
#Component({
selector: 'app-group-details',
templateUrl: 'group-details.component.html',
styleUrls: ['group-details.component.css'],
})
export class GroupDetailsComponent implements OnInit {
#Input()
tageler: Tageler;
tagelers: Tageler[];
group: Group;
constructor(
private route: ActivatedRoute,
private groupService: GroupService,
private tagelerService: TagelerService) {
}
ngOnInit() {
console.log("Init Details");
this.route.params
.switchMap((params: Params) => this.groupService.getGroup(params['id']))
.subscribe(group => this.group = group);
this.tagelerService
.getTagelers()
.then((tagelers: Tageler[]) => {
// some code
}
return tageler;
});
});
}
}
And the test file looks like this:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { GroupDetailsComponent } from './group-details.component';
import { FilterTagelerByGroupPipe } from '../../pipes/filterTagelerByGroup.pipe';
import { SameDateTagelerPipe } from '../../pipes/sameDateTageler.pipe';
import { CurrentTagelerPipe } from '../../pipes/currentTageler.pipe';
import { NextTagelerPipe } from '../../pipes/nextTageler.pipe';
import { RouterTestingModule } from '#angular/router/testing';
import { GroupService } from '../group.service';
import { TagelerService } from '../../tagelers/tageler.service';
describe('GroupDetailsComponent', () => {
let component: GroupDetailsComponent;
let fixture: ComponentFixture<GroupDetailsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
GroupDetailsComponent,
FilterTagelerByGroupPipe,
SameDateTagelerPipe,
CurrentTagelerPipe,
NextTagelerPipe, ],
imports: [ RouterTestingModule ],
providers: [{provide: GroupService}, {provide: TagelerService}],
})
fixture = TestBed.createComponent(GroupDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
class MockGroupService {
getGroups(): Array<Group> {
let toReturn: Array<Group> = [];
toReturn.push(new Group('Trupp', 'Gruppe 1'));
return toReturn;
};
}
it('should create component', () => {
expect(component).toBeDefined();
});
});
I read the angular 2 documentation about testing and a lot of blogs, but I still don't really understand how to test a component that uses services and pipes. When I start the test runner, the test 'should create component' fails and I get the message that my component is not defined (but I don't understand why). I also don't understand how I have to inject the services and pipes. How do I mock them the right way?
I hope that someone can give me helpful advice!
Ramona
You can use spyOn to fake the call in jasmine.
spyOn(yourService, 'method').and.returnValue($q.resolve(yourState));

angular 2 testing mock service

I am trying to mock out a service in my angular 2 test but it just keeps using the real one and giving me loads of errors related to not finding the dependencies of that service
here is my component file
import {Component, Input} from '#angular/core';
import {ClientService} from '../client.service';
#Component({
selector: 'registry',
templateUrl: './registry.component.html',
styleUrls: [
'./registry.component.scss'.toString()
]
})
export class RegistryComponent {
constructor(private clientService: ClientService) {
}
loadResources() {
return this.clientService.getBusinessNetwork()
}
}
And this my test file
import {NO_ERRORS_SCHEMA} from '#angular/core';
import {inject, async, TestBed, ComponentFixture} from '#angular/core/testing';
import {Component} from '#angular/core';
import * as sinon from 'sinon';
import {RegistryComponent} from './registry.component';
import {ClientService} from '../client.service';
describe(`Registry`, () => {
let comp: RegistryComponent;
let fixture: ComponentFixture<RegistryComponent>;
beforeEach(() => {
let MockClientService = {
getBusinessNetwork : sinone.stub()
};
TestBed.configureTestingModule({
declarations: [RegistryComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [
{provide: ClientService, useValue: MockClientService},
]
})
.compileComponents(); // compile template and css
}));
beforeEach(() => {
fixture = TestBed.createComponent(RegistryComponent);
comp = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding
});
it('should have default data', () => {
comp.should.be.true;
});
});
Can anyone help please?

Unit testing component with angulartics2 - Can't bind to 'angularticsCategory' since it isn't a known property of 'div'

I am starting a project using Angular 2.0.0 stable release created with angular-cli 1.0.0-beta.14 and angulartics 1.1.9.
I am trying to start some simple unit testing and am recording clicks on a button component
<div class="sidebar-toggle" (click)="toggleSideBar()" angulartics2On="click" angularticsCategory="{{ expanded ? 'expand': 'collapse' }}">
//divContent
</div>
However, when I run the test which is simple bootstrapping the component I get the error
Can't bind to 'angularticsCategory' since it isn't a known property of 'div'
The app works fine but the issue only comes up in testing. I can't find an example where someone is having the same error in testing. I know I am missing something like not properly exposing the angulartics2 lib in my karma.conf OR not injecting the Angulartics or a mocked dependency in my Testbed config.
Really lost and would like to know if anyone is having similar problems. Can provide more code snippets if needed but don't want to dump the whole file nobody's got time to read that!
In my case to get Angulatics2 unit tests to work I had to:
1) Import Angulartics2Module into the root app.module.ts
import { Angulartics2Module } from 'angulartics2';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
#NgModule({
declarations: [
AppComponent,
HeaderComponent,
],
imports: [
BrowserModule,
Angulartics2Module.forRoot({ developerMode: !environment.production }),
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
2) Enable tracking in app.component.ts
import { Angulartics2GoogleGlobalSiteTag } from 'angulartics2/gst';
import { Component } from '#angular/core';
import { RouterOutlet } from '#angular/router';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
constructor(
public angulartics2GoogleGlobalSiteTag: Angulartics2GoogleGlobalSiteTag,
) {
this.angulartics2GoogleGlobalSiteTag.startTracking();
}
}
3) Import Angulartics2Module.forRoot() and Angulartics2 provider in to app.component.spec.ts
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { TestBed, async } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { Angulartics2, Angulartics2Module } from 'angulartics2';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
Angulartics2Module.forRoot(),
HttpClientTestingModule,
],
declarations: [
AppComponent,
HeaderComponent,
],
providers: [
Angulartics2,
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
and for any other component.spec.ts files that you have using the Angulatics2 directives.

Angular 2 Unit test Error: Cannot resolve all parameters for 'RequestOptions'

I want to test a simple component that have some Dependencies. So among others I have to provide some providers.
/* tslint:disable:no-unused-variable */
import { By } from '#angular/platform-browser';
import { DebugElement, provide } from '#angular/core';
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
fakeAsync,
TestComponentBuilder
} from '#angular/core/testing';
import { AuthHttp, AuthConfig } from 'angular2-jwt';
import { Router, provideRouter } from '#angular/router';
import { Http, ConnectionBackend, RequestOptions, HTTP_PROVIDERS } from '#angular/http';
import { LogoutButtonComponent } from './logout-button.component';
import { UserService } from '../../services/index';
describe('Component: LogoutButtonComponent', () => {
let component: LogoutButtonComponent;
beforeEachProviders(() => [
LogoutButtonComponent,
Http,
provide(AuthHttp, { useFactory: Http }),
provide(AuthConfig, { useValue: new AuthConfig() }),
ConnectionBackend,
RequestOptions,
UserService
]);
beforeEach(inject([AuthHttp, UserService, LogoutButtonComponent],
(comp: LogoutButtonComponent) => {
component = comp;
}));
it('should inject UserService', () => {
// My test here
});
});
Though I'm getting the following error:
Error: Cannot resolve all parameters for 'RequestOptions'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'RequestOptions' is decorated with Injectable.
Am I missing something oin the beforeEachProviders function?
Note: This question is related only with the Unit Testing of Angular 2 with Jasmine. I'm not searching infos relate bootstraping app as this is already ok in my app and there are other related questions here.
You have to import HttpModule into your TestBed configuration.
import { HttpModule } from "#angular/http";
TestBed.configureTestingModule({
imports: [
HttpModule
]
});
After that unit testing should work 👌🏻
RequestOptions is not an injectable, you don't inject this into classes. Instead, you instantiate one as needed when making an HTTP request. So you can remove it from the beforeEachProviders, and instantiate one in the beforeEach if you actually need it in the tests:
let options: RequestOptions;
beforeEach(inject([AuthHttp, UserService, LogoutButtonComponent],
(comp: LogoutButtonComponent) => {
component = comp;
options = new RequestOptions({method: RequestMethod.Post});
}));
I've fixed my error by importing HttpModule and Http from #angular/http :
import {HttpModule, Http} from "#angular/http";
...
TestBed.configureTestingModule({
imports: [HttpModule], // <!-- HTTP module
providers: [HttpService, SourceService, Http] // <!-- HTTP
});
Might be better to mock out the user service then you don't have to worry about the RequestOptions or the HttpModule, here's my approach to problem above:
import { RouterTestingModule } from '#angular/router/testing';
import { TestBed } from '#angular/core/testing';
import { LogoutButtonComponent } from './logout-button.component';
import { UserService } from '../../services/index';
describe('Component: LogoutButtonComponent', () => {
let component: LogoutButtonComponent;
let fixture: ComponentFixture<LogoutButtonComponent>;
let mockUserService;
beforeEach(() => {
// Provide array of user service methods used in LogoutButtonComponent to the createSpyObj
mockUserService = jasmine.createSpyObj(['exampleUserServiceMethod']);
TestBed.configureTestingModule({
declarations: [ LogoutButtonComponent ],
providers: [
{ provide: UserService, useValue: mockUserService }
],
// Only if your component uses routing
imports: [
RouterTestingModule
]
});
fixture = TestBed.createComponent(LogoutButtonComponent);
component = fixture.componentInstance;
})
it('should inject UserService', () => {
// My test here
});
});