Angular 2.0.0 - Ionic 2 RC0 - Npm 3.10.8 - Node v4.5.0 - Karma 1.3.0 - Jasmine 2.5.2
I'm trying to test my application with Karma & Jasmine. Now I got to the point where I followed some guides (I'm new to these testing frameworks). But sadly enough I'm getting an error when trying to execute my test.
I'm trying to test EventsPage
which doesn't have an Http
import but it calls my APICaller.service
which does use Http. That's why I created a MockAPICaller
but it seems to still want Http
(maybe because it is in APICaller
's constructor, but I wouldn't know how to fix that).
So I suspect the problem is within MockAPICaller
but I don't know for sure.
I'll post MockAPICaller.service
, APICaller.service
, EventsPage
and my events.spec.ts
. (in that order so you could maybe skip along if you need/want to.
import { SpyObject } from './helper';
import { APICaller } from '../apicaller.service';
import Spy = jasmine.Spy;
export class MockAPICaller extends SpyObject {
getEventsSpy: Spy;
searchEventSpy:Spy;
getParticipantSpy:Spy;
getEventParticipantsSpy:Spy;
searchEventParticipantSpy:Spy;
addNewCommentSpy:Spy;
updateCommentSpy:Spy;
deleteCommentSpy:Spy;
getUsernameSpy:Spy;
presentSuccessMessageSpy:Spy;
fakeResponse:any;
constructor(){
super( APICaller );
this.fakeResponse = null;
this.getEventsSpy = this.spy('getEvents').andReturn(this);
this.searchEventSpy = this.spy('searchEvent').andReturn(this);
this.getParticipantSpy = this.spy('getParticipant').andReturn(this);
this.getEventParticipantsSpy = this.spy('getEventParticipant').andReturn(this);
this.searchEventParticipantSpy = this.spy('searchEventParticipant').andReturn(this);
this.addNewCommentSpy = this.spy('addNewComment').andReturn(this);
this.updateCommentSpy = this.spy('updateComment').andReturn(this);
this.deleteCommentSpy = this.spy('deleteComment').andReturn(this);
this.getUsernameSpy = this.spy('getUsername').andReturn(this);
this.presentSuccessMessageSpy = this.spy('presentSuccessMessage').andReturn(this);
}
subscribe(callback: any){
callback(this.fakeResponse);
}
setResponse(json:any):void{
this.fakeResponse = json;
}
}
import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { ToastController } from 'ionic-angular';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { Event } from '../models/event.model';
import { Participant } from '../models/participant.model';
import { Comment } from '../models/comment.model';
@Injectable()
export class APICaller {
http : Http;
//baseUrl to the REST API
baseUrl : string = "http://some.correct.url:8080/myAPI";
constructor(public httpService: Http, public toastCtrl:ToastController) {
this.http = httpService;
}
//-------------------EVENTS-----------------------------------//
//retrieves all the events
getEvents() : Observable<Array<Event>> {
return this.http
.get(`${ this.baseUrl }/events`)
.map(response => {
return response.json();
});
}
//searches events with the provided term
searchEvent(searchTerm : string) : Observable<Array<Event>> {
return this.http
.get(`${ this.baseUrl }/events/search/${ searchTerm }`)
.map(response => {
return response.json();
});
}
//--------------------PARTICIPANTS-----------------------------------//
//retrieves the participant from the REST API
getParticipant(participantId : number) : Observable<Participant>{
return this.http
.get(`${ this.baseUrl }/participants/${ participantId }`)
.map(response => {
return response.json();
});
}
getEventParticipants(eventId:number) : Observable<Array<Participant>> {
return this.http
.get(`${ this.baseUrl }/events/${ eventId }/participants`)
.map(response => {
return response.json();
});
}
//searches for deelnemers with the provided term
searchEventParticipant(eventId : number, searchTerm : string) : Observable<Array<Participant>> {
return this.http
.get(`${ this.baseUrl }/events/${ eventId }/participants/search/${ searchTerm }`)
.map(response => {
return response.json();
});
}
//-------------------COMMENTS--------------------------------------//
//adding a new comment to a participant
addNewComment(participantId : number, content : string) : Observable<Comment> {
return this.http
.post(`${ this.baseUrl }/participants/${ participantId }/addComment`
,{
user: this.getUsername("apikey"),
content: content
}).map((response) => {
this.presentSuccessMessage("Comment added");
return (response.json());
});
}
//updating an existing comment
updateComment(participantId : number, commentId : number, content : string) : Observable<Comment> {
return this.http
.put(`${ this.baseUrl }/participants/${ participantId }/updateComment/${ commentId }`,{
id: commentId,
content: content
}).map(response => {
this.presentSuccessMessage("Comment updated");
return response.json();
});
}
//deleting a currently existing comment
deleteComment(participantId : number, commentId : number) : Observable<Comment> {
return this.http
.delete(`${ this.baseUrl }/participants/${ participantId }/deleteComment/${ commentId }`)
.map(response => {
this.presentSuccessMessage("Comment deleted");
return response.json();
});
}
//presents a successmessage for 3 seconds
presentSuccessMessage(messageContent : string) {
//defining the message
let message = this.toastCtrl
.create({
message: messageContent,
duration: 3000
});
//showing the message on screen
message.present();
}
//-------------------USER-------------------------------
getUsername(someRandomKey : string) : string {
return "developer";
/*
return this.http
.get(`${ this.baseUrl }/getUsername/${ someRandomKey }`)
.map(response => {
return ;
});
*/
}
}
import { Component } from '@angular/core';
import { NavController, Loading, LoadingController } from 'ionic-angular';
import { APICaller } from '../../services/apicaller.service';
import { EventDetailComponent } from '../event-detail/event-detail.component';
import { Event } from '../../models/event.model';
/*
Class for Evenementen Overzicht.
*/
@Component({
selector: 'events-component',
templateUrl: 'events.component.html',
providers: [ APICaller ]
})
/** -------------------------------------------------------------------------------------- */
export class EventsPage {
//list of all events
public events : Array<Event>;
//the event that has been clicked on the page
public selectedEvent : Event;
//boolean to show 'no events' error message
public noEvents:boolean;
/** -------------------------------------------------------------------------------------- */
constructor(public navCtrl : NavController, public apiCaller:APICaller, public loadingCtrl : LoadingController) {
//retrieve all events --> async method, can't use this.events yet.
this.getEvents();
}
/** -------------------------------------------------------------------------------------- */
/**Get Events - Sets the 'events' variable to all events found by the API. */
getEvents(){
//setup a loadingscreen
let loading = this.loadingCtrl.create({
content: "Loading..."
});
//present the loadingscreen
loading.present();
//reset the noEvents boolean.
this.noEvents = true;
//call the api and get all events
this.apiCaller.getEvents()
.subscribe(response => {
//response is list of events
this.events = response;
//if the event is not empty, set noEvents to false.
if(this.events.length > 0){
this.noEvents = false;
}
//close the loading message.
loading.dismiss();
});
}
/** -------------------------------------------------------------------------------------- */
/**Select Event - Sets the selectedEvent variable to the selected item. */
selectEvent(event: any, eventObj){
this.selectedEvent = eventObj;
}
/**Search Events - Triggers the API and sets events equal to events found by API*/
searchEvents(ev){
//reset noEvents
this.noEvents = true;
//if the searchfield is not empty, call api
if(ev.target.value != ''){
this.apiCaller.searchEvent(ev.target.value)
.subscribe(response => {
this.events = response;
//
if(this.events.length > 0){
this.noEvents = false;
}
});
}else{
//if the searchfield is empty, get all the events
this.getEvents();
}
}
/** -------------------------------------------------------------------------------------- */
/*Cancel Search - clears input field and resets noEvents*/
cancelSearch(ev){
ev.target.value = "";
this.noEvents = false;
}
/** -------------------------------------------------------------------------------------- */
/**Do Refresh - Refreshes the list of */
doRefresh(refresher) {
this.getEvents();
//giving feedback for user (1 sec instead of faster)
setTimeout(() => {
//stop the refresher
refresher.complete();
}, 1000);
}
/** -------------------------------------------------------------------------------------- */
/**Go to EventDetail - Pushes the EventDetail page on the navigation stack. */
goToEventDetail(eventOb: any, eventParam){
this.navCtrl.push(EventDetailComponent
, {
event: eventParam
});
}
}
/** -------------------------------------------------------------------------------------- */
import { TestBed, inject, tick, fakeAsync } from '@angular/core/testing';
import { BaseRequestOptions, Http, ConnectionBackend, Response, ResponseOptions} from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { FormsModule } from '@angular/forms';
import { NavController, LoadingController } from 'ionic-angular';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { mockNavController } from 'ionic-angular/util/mock-providers';
import { EventsPage } from './events.component';
import { MockAPICaller } from '../../services/mocks/apicaller.service';
import { APICaller } from '../../services/apicaller.service';
describe('Component: EventsComponent', () => {
let mockAPICaller : MockAPICaller = new MockAPICaller();
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [EventsPage],
schemas: [CUSTOM_ELEMENTS_SCHEMA],//for usage of Ionic2
providers: [
{provide: NavController, useValue: mockNavController },
{provide: LoadingController, useValue: LoadingController},
{provide: APICaller, useValue: mockAPICaller}
],
imports: [FormsModule]
});
});
it('should return all events', ()=>{
let fixture = TestBed.createComponent(EventsPage);
let eventsPage = fixture.debugElement.componentInstance;
fixture.detectChanges();
mockAPICaller.setResponse(JSON.stringify(`{
id: 4,
title: 'Weekend',
eventdate: '24/09/2016',
kind: 'closed',
startingtime: '18:00',
endtime: '21:00',
description: 'Go home'
}`));
let results = eventsPage.getEvents();
expect(results.length).toBe(1);
expect(results[0].id).toBe(4);
});
});
The problem is this
@Component({
providers: [ APICaller ] <========
})
export class EventsPage {
By having that, the component will try to create its own instance of the APICaller
. This overrides any configurations you make in the TestBed
(i.e. the mock).
What you can do is override the component before you create it
beforeEach(() => {
TestBed.configureTestingModule({})
TestBed.overrideComponent(EventsPage, {
set: {
providers: [
{ provide: APICaller, useValue: mockApiCaller }
]
}
})
})
See Also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With