UnitTest Mock Http response in Angular2 (2.1.0) Service - unit-testing

I am very confused by the different sources online about testing Angular2, mainly due to differences in versions. I am using Angular 2.1.0 final and I don't understand how to mock a simple http response for my service test class below:
import { TestBed, async, inject } from '#angular/core/testing';
import { PersonService } from '../services/person.service';
import { Person} from '../models/Person';
import { MOCK_DATA_PERSON } from '../test/mocks/mock-data-person';
// HTTP mocking imports
import { Http, BaseRequestOptions, Response, HttpModule, ResponseOptions } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
describe('PersonService', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
PersonService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend: MockBackend, options: BaseRequestOptions) => {
return new Http(backend, options);
},
deps: [MockBackend, BaseRequestOptions],
}
],
imports: [
HttpModule
]
});
TestBed.compileComponents();
}));
it('returns a list of persons', async(inject([MockBackend, PersonService], (backend: MockBackend, service) => {
backend.connections.subscribe(
(connection: MockConnection) => {
connection.mockRespond(new Response(
new ResponseOptions({
body: JSON.stringify({name: "Bob", surname : "Jones"})
})));
});
service.getPersons()
.subscribe(persons=> {
expect(persons.length).toBeDefined();
expect(persons.length).toBe(1);
}).catch(error => console.log(error));
})));
});
given the getPersons method looks like:
getPersons (): Observable<Person[]> {
return this.http.get(this.getAllUrl)
.map(res.json().data)
.catch(this.handleError);
}
i am getting the error "Failed: this.http.get(...).map is not a function"

Test is good, but the Observable.map operator isn't found. Try to just import it
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

Related

unit testing a component which is injected by a service which is using angular2-jwt AuthHttp call instead of http call

I have been using HTTP to call API to do my stuff, later introduced angular2-jwt to intercept the http calls and pass the jwt tockens. Unit testing was working fine until AuthHttp was used. I have created 'mockuserdata' model to return the mock values
Every unit test cases where failing until I added the providers for http and AuthHttp as follows
{provide: Http, useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) =>
{ return new Http(backendInstance, defaultOptions);},
deps: [MockBackend, BaseRequestOptions] },
{ provide: AuthHttp, useExisting: Http, deps: [Http] }
After introducing above providers 'should instantiate UsersComponent' got passed but 'It checks loadUserList() is binding datasource' ís failing
/* User Operations Service*/
import {Headers,RequestOptionsArgs,Response,URLSearchParams, RequestMethod,RequestOptions }from '#angular/http';
import { Injectable, Optional }from '#angular/core';
import { Observable}from 'rxjs/Observable';
import * as models from '../models';
import { AuthHttp }from 'angular2-jwt';
import 'rxjs/Rx';
# Injectable()
export class UserOperationsApi {
protected basePath = '/api';
public defaultHeaders: Headers = new Headers();
constructor(protected http: AuthHttp, # Optional()basePath: string) {
if (basePath) {
this.basePath = basePath;
}
}
/**
* Returns all users
* Returns list of all users
*/
public getAllUsers(extraHttpRequestParams ? : any): Observable < Array < models.User >> {
const path = this.basePath + '/users';
let queryParameters = new URLSearchParams();
let headerParams = this.defaultHeaders;
let requestOptions: RequestOptionsArgs = {
method: 'GET',
headers: headerParams,
search: queryParameters
};
return this.http.request(path, requestOptions)
.map((response: Response) => {
if (response.status === 204) {
return undefined;
} else {
return response.json();
}
});
}
}
/* User List Component*/
import {Component,OnInit}from '#angular/core';
import {Router } from '#angular/router';
import {UserOperationsApi }from '../services';
import {DialogService }from '../services';
import { Subscription }from 'rxjs';
# Component({
selector: 'my-app',
templateUrl: './list-users.component.html',
providers: [UserOperationsApi]
})
export class UsersComponent implements OnInit {
public userData: any;
constructor(private api: UserOperationsApi, private router: Router) {
this.loadUserList();
}
// loading user list
loadUserList() {
// To get data from api
this.busy = this.api.getAllUsers()
.subscribe(
data => this.handleConfiguredUsers(data),
error => {
throw ({
message: 'Error occured! while retreiving data from server.'
});
});
}
// users list to be loaded
handleConfiguredUsers(data) {
this.userData = data;
}
}
** * User component unit testing ** ** **
import {async,TestBed,inject}from '#angular/core/testing';
import {Response,ResponseOptions, XHRBackend } from '#angular/http';
import { MockBackend, MockConnection}from '#angular/http/testing';
import { HttpModule}from '#angular/http';
import { Router, ActivatedRoute}from '#angular/router';
import { MockRouter}from '../mock/router.mock';
import { usersData}from '../data/mockData/users';
import { UsersComponent}from './list-users.component';
describe('list-users.component.ts', () => {
beforeEach(() => {
TestBed.configureTestingModule(
{
declarations: [UsersComponent],
imports: [HttpModule, DevExtremeModule],
providers: [MockBackend, BaseRequestOptions,
{ provide: XHRBackend, useClass: MockBackend },
{ provide: Router, useClass: MockRouter },
{ provide: ActivatedRoute, useClass: MockRouter },
{
provide: Http, useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backendInstance, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
{ provide: AuthHttp, useExisting: Http, deps: [Http] }
]
});
});
it('should instantiate UsersComponent', () => {
const fixture = TestBed.createComponent(UsersComponent);
let compInstance: UsersComponent = fixture.componentInstance;
expect(compInstance instanceof UsersComponent).toBe(true, 'should create UsersComponent');
});
it('It checks loadUserList() is binding datasource',
async(inject([XHRBackend],
(mockBackend: MockBackend) => {
mockBackend.connections.subscribe(
(connection: MockConnection) => {
connection.mockRespond(new Response(
new ResponseOptions({
body: usersData
})));
});
const fixture = TestBed.createComponent(UsersComponent);
let instance: UsersComponent = fixture.componentInstance;
instance.instance.loadUserList();;
expect(instance.userData.length).toEqual(usersData.length);
expect(instance.userData).toBe(usersData);
})));
}
1) Whats wrong with the unit test which is returning null values.
The following worked for me. instruct to use Http instead of AuthHttp using
{ provide: AuthHttp, useExisting: Http }
so my unit testing file has the following change.
describe('list-users.component.ts', () => {
beforeEach(() => {
TestBed.configureTestingModule(
{
declarations: [UsersComponent],
imports: [HttpModule, DevExtremeModule],
providers: [MockBackend, BaseRequestOptions,
{ provide: XHRBackend, useClass: MockBackend },
{ provide: Router, useClass: MockRouter },
{ provide: ActivatedRoute, useClass: MockRouter },
{ provide: AuthHttp, useExisting: Http }
]
});
});
Hope it will be useful for someone someday, Cheers :)

Angular 2 Observable Service Karma Jasmine Unit Test not working

I am a newbie to Angular 2 and Karma + Jasmine unit tests. I cannot figure out what semantic error I have made in order to make this unit test use the mocked response. In the console, when "expect(items[0].itemId).toBe(2);" is run, it says items[0].itemId is undefined.
Would someone be able to help me out or point me in the right direction? Please let me know if you need any additional information. Thanks!
item.ts
export class Item {
itemId: number;
itemName: string;
itemDescription: string;
}
item.service.ts
import { Injectable, Inject } from '#angular/core';
import { Headers, Http } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { Item } from './item';
#Injectable()
export class ItemService {
private headers = new Headers({'Content-Type': 'application/json'});
constructor(
private http: Http)
{
}
getItems(listOptions: Object): Observable<Item[]> {
return this.http.post('/listItems', listOptions, {headers:this.headers})
.map(response => response.json() as Item[])
}
}
item.service.spec.ts
import { TestBed, fakeAsync, inject, tick } from '#angular/core/testing';
import { MockBackend } from '#angular/http/testing';
import { Http, BaseRequestOptions, Response, ResponseOptions } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { ItemService } from './item.service';
import { Item } from './item';
describe('ItemService', () => {
let mockResponse, matchingItem, connection;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ItemService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend, defaultOptions) => new Http(backend, defaultOptions),
deps: [MockBackend, BaseRequestOptions]
},
// { provide: XHRBackend, useClass: MockBackend }
]
});
const items = [
{
"itemId":2,
"itemName":"test item1",
"itemDescription":"hello hello"
},
{
"itemId":1,
"itemName":"name2124111121",
"itemDescription":"description212412112"
}
];
mockResponse = new Response(new ResponseOptions({body: {data: items}, status: 200}));
});
describe('getItems', () => {
//Subscribing to the connection and storing it for later
it('should return all the items',inject([ItemService, MockBackend], (service: ItemService, backend: MockBackend) => {
backend.connections.subscribe(connection => {
connection.mockRespond(mockResponse);
});
service.getItems({isActive: true, sortColumn: "lastModifiedDateUtc", sortOrder: "desc"})
.subscribe((items: Item[]) => {
expect(items.length).toBe(2);
});
}));
});
});
Plunkr: https://plnkr.co/edit/m7In2eVh6oXu8VNYFf9l?p=preview
(There are some errors with the Plunkr I need help with as well but the main files are there)
The mockResponse body did not match the actual response body, that is why I was getting the error.
mockResponse = new Response(new ResponseOptions({body: {data: items}, status: 200})); should be mockResponse = new Response(new ResponseOptions({body: items, status: 200}));

Karma+jasmine+Angular2 unit testing http service error (No provider for [object Object])

I have set up a simple service and tried to run the test.
Service
//CustomerServiceTest.ts:
import { Injectable, Pipe } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
#Injectable()
export class CustomerServiceTest{
constructor(public http: Http) {}
query(URL: string, params?: Array<string>): Observable<any[]> {
let be_endpoint: string = 'http:\/\/localhost:44444\/';
let queryURL: string = `${be_endpoint}${URL}`;
if (params) {
queryURL = `${queryURL}?${params.join('&')}`;
}
return this.http.request(queryURL).map((res: any) => res.json());
}
searchBackEnd(query: string, type: string): Observable<any[]> {
return this.query(`/${type}`, [
`id=${query}`
]);
}
}
Test for the service
//CustomerServiceTest.spec.ts:
import {
TestBed,
getTestBed,
async,
inject
} from '#angular/core/testing';
import {
Headers, BaseRequestOptions,
Response, HttpModule, Http, XHRBackend, RequestMethod
} from '#angular/http';
import {ResponseOptions} from '#angular/http';
import {MockBackend, MockConnection} from '#angular/http/testing';
import { CustomerServiceTest } from '../../ts/components/CustomerServiceTest';
describe('CustomerServiceTest test suite', () => {
let mockBackend: MockBackend;
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
CustomerServiceTest,
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory:
(backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
],
imports: [
HttpModule
]
}).compileComponents();
mockBackend = getTestBed().get(MockBackend);
}));
it('should get data', async(inject([CustomerServiceTest], (CustomerServiceTest) => {
let crmSvc:CustomerServiceTest;
let ContactResponceBody:Object = {
body: [
{
id: '12345678',
cellPhNum: '+9273839222',
lastName: 'TestUser',
firstName: 'TestUser'
}
]
};
let contactResponceOptions:ResponseOptions = new ResponseOptions(ContactResponceBody);
let contactResponse:Response = new Response(contactResponceOptions);
mockBackend.connections.subscribe(
(connection: MockConnection) => {
connection.mockRespond(contactResponse);
});
crmSvc = getTestBed().get(CustomerServiceTest);
expect(crmSvc).toBeDefined();
})));
});
Executing npm run test results in error:
Chrome 49.0.2623 (Windows 7 0.0.0) CustomerServiceTest test suite should get data FAILED
Failed: No provider for [object Object]!
Error: DI Error
at NoProviderError.Error (native)
at NoProviderError.ZoneAwareError (karma.entry.js:43703:33)
at NoProviderError.BaseError [as constructor] (karma.entry.js:6062:34)
at NoProviderError.AbstractProviderError [as constructor] (karma.entry.js:32848:16)
at new NoProviderError (karma.entry.js:32879:16)
at ReflectiveInjector_._throwOrNull (karma.entry.js:53504:19)
at ReflectiveInjector_._getByKeyDefault (karma.entry.js:53532:25)
at ReflectiveInjector_._getByKey (karma.entry.js:53495:25)
at ReflectiveInjector_.get (karma.entry.js:53304:21)
at TestBed.get (karma.entry.js:9842:67)
Apparently, some dependency is missing, but I can't understand what it is. Could anyone explain where to look? At first glance - all providers are at place...
Worked it out:
//CustomerService.spec.ts
import {
TestBed,
getTestBed,
async,
inject
} from '#angular/core/testing';
import {
Headers, BaseRequestOptions,
Response, HttpModule, Http, XHRBackend, RequestMethod
} from '#angular/http';
import {ResponseOptions} from '#angular/http';
import {MockBackend, MockConnection} from '#angular/http/testing';
import { CustomerServiceTest } from '../../ts/components/CustomerServiceTest';
describe('CustomerServiceTest:', () => {
let mockBackend: MockBackend;
let ContactResponceBody:Object = {
body: [
{
id: '1-DR8C2SJ',
cellPhNum: '1111',
lastName: 'qqq',
firstName: 'www',
midName: 'eee',
sex: 'М',
age: 23,
email: 'test#test.com',
localtime: '12:00',
utcOffset:180
}
]
};
let contactResponceOptions:ResponseOptions = new ResponseOptions(ContactResponceBody);
let contactResponse:Response = new Response(contactResponceOptions);
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
CustomerServiceTest,
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory:
(backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
],
imports: [
HttpModule
]
});
mockBackend = getTestBed().get(MockBackend);
}));
it('Available',
async(
inject(
[CustomerServiceTest],
(custSvc:CustomerServiceTest) => {
expect(custSvc).toBeDefined();
}
)
)
)
it('Returns data',
async(
inject(
[TCSCustomerServiceTest],
(custSvc:TCSCustomerServiceTest) => {
//emulate contact response from real back-end
mockBackend.connections.subscribe(
(connection: MockConnection) => {
connection.mockRespond(contactResponse);
});
custSvc.searchBackEnd('1-DR8C2SJ', 'contact').subscribe(
(response: any) => {
expect(response[0].id).toBe('1-DR8C2SJ');
expect(response[0].cellPhNum).toBe('1111');
expect(response[0].lastName).toBe('qqq');
expect(response[0].firstName).toBe('www');
expect(response[0].midName).toBe('eee');
expect(response[0].sex).toBe('М');
expect(response[0].age).toBe(23);
expect(response[0].email).toBe('test#test.com');
expect(response[0].localtime).toBe('12:00');
expect(response[0].utcOffset).toBe(180);
}
);
}
)
)
)
});

mockRespond method inside beforeEach invokes later than XHR to backend inside test

I am trying to write Jasmine tests using MockBackend from Angular2.
Here is the code that I write to test a service which communicate with backend:
import { inject, TestBed } from '#angular/core/testing';
import {VehicleCategoryService} from './VehicleCategoryService';
import { BaseRequestOptions, Response, ResponseOptions, Http } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
import {HttpService} from "../../api/HttpService";
import {ApplicationService} from "../../api/ApplicationService";
describe('VehicleCategoryService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [VehicleCategoryService,BaseRequestOptions, MockBackend, ApplicationService, {
provide: HttpService,
useFactory: (backend, options, applicationService) => {
return new HttpService(backend, options, applicationService);
},
deps: [MockBackend, BaseRequestOptions, ApplicationService],
}],
});
});
beforeEach(inject([MockBackend], (backend: MockBackend) => {
const baseResponse = new Response(new ResponseOptions({ body: '{"Name":"MiniVan"}' }));
backend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse));
}));
it('should be defined', inject([VehicleCategoryService], (vehicleCategoryService: VehicleCategoryService) => {
expect(vehicleCategoryService).toBeDefined();
}));
it('should return response when querying vehicleCategories', inject([VehicleCategoryService], (vehicleCategoryService: VehicleCategoryService) => {
vehicleCategoryService.query().subscribe((res: Response) => {
expect(res['Name']).toBe('MiniVan');
});
}));
});
http://stackoverflow.com/questions/ask#
HttpService here is a service that extends from built-in Http Angular2 service. query() method returns Observable as a usual Http do.
Debugging issue demonstarte that mockRespond() method inside subscription invokes later than actual call to backend that is why I receive undefined response. How can I solve this problem?

How to use MockBackend in Angular2

I have this service I need to test:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
#Injectable()
export class LoginService {
private baseUrl: string = 'http://localhost:4000/';
constructor (private http: Http) {}
public getBaseUrl() {
return this.baseUrl;
}
getLogin() {
return this.http.get(this.baseUrl + 'api/auth/octopus')
.map(res => res.json().redirect);
}
}
To test the getLogin() function I have this code:
import {
async,
describe,
it,
expect,
beforeEach,
addProviders,
inject
} from '#angular/core/testing';
import { provide} from '#angular/core';
import { LoginService } from './login.service';
import {
Http,
BaseRequestOptions,
} from '#angular/http';
import {MockBackend} from '#angular/http/testing';
describe('Service: LoginService', () => {
beforeEach(() => addProviders([
LoginService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
})
]));
it('should return data.',
async(inject([LoginService], (loginService: LoginService) => {
loginService.getLogin().subscribe(
data => console.log(data)
);
})
));
});
However, the data doesn't get logged.
I tried various solutions that I found on SO or on the internet.
One of them was to make a http call to the mockbackend but that just gave me data = undefined.
You need to inject MockBackend and subscribe for connection, like this:
UPDATE
async(inject([LoginService, MockBackend], (loginService: LoginService, mockBackend:MockBackend) => {
let mockResponse = new Response(new ResponseOptions({body: {'redirect': 'some string'}}))
mockBackend.connections.subscribe(c => c.mockRespond(mockResponse));
loginService.getLogin().subscribe(
data => console.log(data)
);
})
For more details go here:
https://angular.io/docs/ts/latest/api/http/testing/index/MockBackend-class.html