I write test cases in angular 2.I want to know how following thing done. please help me.
How to define Mocking (Http, router) globally.
How to call common beforeEach() in every test cases.
My code which i want to common.
common.ts
/* this used for route Mocking
* */
let mockRouter = {
navigate: jasmine.createSpy('navigate')
}
/*
* Response Url
* */
let metaUrl = {
LOGIN: '/operator/login',
META_API: '/meta-api'
};
let subject:GlobalUtils = null;
let backend:MockBackend = null;
let http:Http = null;
/*
* The beforeEach function is called once before each spec in the describe in which it is called
* */
beforeEach(()=>TestBed.configureTestingModule({
providers: [
GlobalUtils,
BaseRequestOptions,
MockBackend,
{
provide: Router, useValue: mockRouter
},
{
provide: Http,
useFactory: function (backend:ConnectionBackend, defaultOptions:BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
]
}));
beforeEach(inject([GlobalUtils, MockBackend, Http], (_globalUtils:GlobalUtils, mockBackend:MockBackend, _http:Http) => {
subject = _globalUtils;
backend = mockBackend;
http = _http;
}));
Reason behind this I exactly this in another spec.ts file for write test case.
here is my spec.ts files
1.spec.ts
import {Http, BaseRequestOptions, Response, ResponseOptions, ConnectionBackend} from '#angular/http';
import {MockBackend, MockConnection} from '#angular/http/testing';
import {Router} from '#angular/router';
import {TestBed, inject} from '#angular/core/testing';
import {GlobalUtils} from '../global.utils.ts';
import { Constant } from '../constant'
describe('Global Utils : Meta urls API', () => {
/* this used for route Mocking
* */
let mockRouter = {
navigate: jasmine.createSpy('navigate')
}
/*
* Response Url
* */
let metaUrl = {
LOGIN: '/operator/login',
META_API: '/meta-api'
};
let subject:GlobalUtils = null;
let backend:MockBackend = null;
let http:Http = null;
/*
* The beforeEach function is called once before each spec in the describe in which it is called
* */
beforeEach(()=>TestBed.configureTestingModule({
providers: [
GlobalUtils,
BaseRequestOptions,
MockBackend,
{
provide: Router, useValue: mockRouter
},
{
provide: Http,
useFactory: function (backend:ConnectionBackend, defaultOptions:BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
]
}));
beforeEach(inject([GlobalUtils, MockBackend, Http], (_globalUtils:GlobalUtils, mockBackend:MockBackend, _http:Http) => {
subject = _globalUtils;
backend = mockBackend;
http = _http;
}));
/*
* This Test case is check Response of getMetaUrls function
* getMetaUrls must return response
* */
it('Should have get Meta urls', (done) => {
// HTTP Mocking
backend.connections.subscribe((connection:MockConnection)=> {
let options = new ResponseOptions({
body: JSON.stringify(metaUrl),status: 200
});
connection.mockRespond(new Response(options));
});
/*
* we mock http get method
* */
http
.get(Constant.metaUrl)
.subscribe((response) => {
subject.getMetaUrls();
expect(Constant.saveUrl).toEqual(metaUrl);
done();
});
});
/*
* This Test case is check empty response and redirect to error page
* getMetaUrls must return empty response
* */
it('Should have get Empty Meta urls data', (done) => {
backend.connections.subscribe((connection:MockConnection)=> {
let options = new ResponseOptions({
body: JSON.stringify(''), status: 200
});
connection.mockRespond(new Response(options));
});
http
.get(Constant.metaUrl)
.subscribe((response) => {
subject.getMetaUrls();
expect(mockRouter.navigate).toHaveBeenCalledWith(['page-error']);
done();
});
});
2.spec.ts
describe('Authentication test',()=>{
});
2.spec file blank now because I want to use common.ts function in this file and I want to know how.
Related
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 :)
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}));
I am trying to write unit tests for my API service but have some trouble catching HTTP errors. I am following this guide along with the Angular2 docs since the guide is (slightly) out of date in some minor areas.
All unit tests pass apart from those where an error is thrown by the service (due to error HTTP status code). I can tell this by logging out response.ok. From what i've read this has something to do with the unit tests not executing asynchronously, hence, not waiting for the error response. However, I have no idea why this is the case here since I have used the async() utility function in the beforeEach method.
API Service
get(endpoint: string, authenticated: boolean = false): Observable<any> {
endpoint = this.formatEndpoint(endpoint);
return this.getHttp(authenticated) // Returns #angular/http or a wrapper for handling auth headers
.get(endpoint)
.map(res => this.extractData(res))
.catch(err => this.handleError(err)); // Not in guide but should work as per docs
}
private extractData(res: Response): any {
let body: any = res.json();
return body || { };
}
private handleError(error: Response | any): Observable<any> {
// TODO: Use a remote logging infrastructure
// TODO: User error notifications
let errMsg: string;
if (error instanceof Response) {
const body: any = error.json() || '';
const err: string = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''}${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
Error unit test
// Imports
describe('Service: APIService', () => {
let backend: MockBackend;
let service: APIService;
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
BaseRequestOptions,
MockBackend,
APIService,
{
deps: [
MockBackend,
BaseRequestOptions
],
provide: Http,
useFactory: (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
},
{provide: AuthHttp,
useFactory: (http: Http, options: BaseRequestOptions) => {
return new AuthHttp(new AuthConfig({}), http, options);
},
deps: [Http, BaseRequestOptions]
}
]
});
const testbed: any = getTestBed();
backend = testbed.get(MockBackend);
service = testbed.get(APIService);
}));
/**
* Utility function to setup the mock connection with the required options
* #param backend
* #param options
*/
function setupConnections(backend: MockBackend, options: any): any {
backend.connections.subscribe((connection: MockConnection) => {
const responseOptions: any = new ResponseOptions(options);
const response: any = new Response(responseOptions);
console.log(response.ok); // Will return false during the error unit test and true in others (if spyOn log is commented).
connection.mockRespond(response);
});
}
it('should log an error to the console on error', () => {
setupConnections(backend, {
body: { error: `Some strange error` },
status: 400
});
spyOn(console, 'error');
spyOn(console, 'log');
service.get('/bad').subscribe(null, e => {
// None of this code block is executed.
expect(console.error).toHaveBeenCalledWith("400 - Some strange error");
console.log("Make sure an error has been thrown");
});
expect(console.log).toHaveBeenCalledWith("Make sure an error has been thrown."); // Fails
});
Update 1
when I check the first callback, response.ok is undefined. This leads me to believe that there is something wrong in the setupConnections utility.
it('should log an error to the console on error', async(() => {
setupConnections(backend, {
body: { error: `Some strange error` },
status: 400
});
spyOn(console, 'error');
//spyOn(console, 'log');
service.get('/bad').subscribe(res => {
console.log(res); // Object{error: 'Some strange error'}
console.log(res.ok); // undefined
}, e => {
expect(console.error).toHaveBeenCalledWith("400 - Some strange error");
console.log("Make sure an error has been thrown");
});
expect(console.log).toHaveBeenCalledWith("Make sure an error has been thrown.");
}));
Update 2
If, instead of catching errors in the get method I do it explicitly in map then still have same problem.
get(endpoint: string, authenticated: boolean = false): Observable<any> {
endpoint = this.formatEndpoint(endpoint);
return this.getHttp(authenticated).get(endpoint)
.map(res => {
if (res.ok) return this.extractData(res);
return this.handleError(res);
})
.catch(this.handleError);
}
Update 3
After some discussion this issue submitted
Here is my working solution which is similar to above suggestions but with more clarity:
it('should log an error to the console on error', async(inject([AjaxService, MockBackend], (
ajaxService: AjaxService, mockBackend: MockBackend) => {
service = ajaxService;
backend = mockBackend;
backend.connections.subscribe((connection: MockConnection) => {
const options: any = new ResponseOptions({
body: { error: 'Some strange error' },
status: 404
});
const response: any = new Response(options);
connection.mockError(response);
});
spyOn(console, 'error');
service.get('/bad').subscribe(res => {
console.log(res); // Object{error: 'Some strange error'}
}, e => {
expect(console.error).toHaveBeenCalledWith('404 - Some strange error');
});
})));
Reference full working code:
Below are all possible test scenarios.
Note: Don't worry about AjaxService. It's my custom wrapper on angular http service which is being used as a interceptor.
ajax.service.spec.ts
import { AjaxService } from 'app/shared/ajax.service';
import { TestBed, inject, async } from '#angular/core/testing';
import { Http, BaseRequestOptions, ResponseOptions, Response } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
describe('AjaxService', () => {
let service: AjaxService = null;
let backend: MockBackend = null;
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backendInstance, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
AjaxService
]
});
}));
it('should return mocked post data',
async(inject([AjaxService, MockBackend], (
ajaxService: AjaxService, mockBackend: MockBackend) => {
service = ajaxService;
backend = mockBackend;
backend.connections.subscribe((connection: MockConnection) => {
const options = new ResponseOptions({
body: JSON.stringify({ data: 1 }),
});
connection.mockRespond(new Response(options));
});
const reqOptions = new BaseRequestOptions();
reqOptions.headers.append('Content-Type', 'application/json');
service.post('', '', reqOptions)
.subscribe(r => {
const out: any = r;
expect(out).toBe(1);
});
})));
it('should log an error to the console on error', async(inject([AjaxService, MockBackend], (
ajaxService: AjaxService, mockBackend: MockBackend) => {
service = ajaxService;
backend = mockBackend;
backend.connections.subscribe((connection: MockConnection) => {
const options: any = new ResponseOptions({
body: { error: 'Some strange error' },
status: 404
});
const response: any = new Response(options);
connection.mockError(response);
});
spyOn(console, 'error');
service.get('/bad').subscribe(res => {
console.log(res); // Object{error: 'Some strange error'}
}, e => {
expect(console.error).toHaveBeenCalledWith('404 - Some strange error');
});
})));
it('should extract mocked data with null response',
async(inject([AjaxService, MockBackend], (
ajaxService: AjaxService, mockBackend: MockBackend) => {
service = ajaxService;
backend = mockBackend;
backend.connections.subscribe((connection: MockConnection) => {
const options = new ResponseOptions({
});
connection.mockRespond(new Response(options));
});
const reqOptions = new BaseRequestOptions();
reqOptions.headers.append('Content-Type', 'application/json');
service.get('test', reqOptions)
.subscribe(r => {
const out: any = r;
expect(out).toBeNull('extractData method failed');
});
})));
it('should log an error to the console with empty response', async(inject([AjaxService, MockBackend], (
ajaxService: AjaxService, mockBackend: MockBackend) => {
service = ajaxService;
backend = mockBackend;
backend.connections.subscribe((connection: MockConnection) => {
const options: any = new ResponseOptions({
body: {},
status: 404
});
const response: any = new Response(options);
connection.mockError(response);
});
spyOn(console, 'error');
service.get('/bad').subscribe(res => {
console.log(res); // Object{error: 'Some strange error'}
}, e => {
expect(console.error).toHaveBeenCalledWith('404 - {}');
});
// handle null response in error
backend.connections.subscribe((connection: MockConnection) => {
connection.mockError();
});
const res: any = null;
service.get('/bad').subscribe(res, e => {
console.log(res);
}, () => {
expect(console.error).toHaveBeenCalledWith(null, 'handleError method with null error response got failed');
});
})));
});
ajax.service.ts
import { Injectable } from '#angular/core';
import { Http, Response, RequestOptionsArgs, BaseRequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
/**
* Wrapper around http, use this for all http operations.
* It has centralized error handling as well.
* #export
* #class AjaxService
*/
#Injectable()
export class AjaxService {
/**
* Creates an instance of AjaxService.
* #param {Http} http
*
* #memberOf AjaxService
*/
constructor(
private http: Http,
) { }
/**
* Performs a request with get http method.
*
* #param {string} url
* #param {RequestOptionsArgs} [options]
* #returns {Observable<Response>}
*
* #memberOf AjaxService
*/
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
options = this.getBaseRequestOptions(options);
options = this.setHeaders(options);
return this.http.get(url, options)
.map(this.extractData)
.catch(this.handleError);
}
/**
* Performs a request with post http method.
*
* #param {string} url
* #param {*} body
* #param {RequestOptionsArgs} [options]
* #returns {Observable<Response>}
*
* #memberOf AjaxService
*/
post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
options = this.getBaseRequestOptions(options);
options = this.setHeaders(options);
return this.http.post(url, body, options)
.map(this.extractData)
.catch(this.handleError);
}
/**
* Util function to fetch data from ajax response
*
* #param {Response} res
* #returns
*
* #memberOf AjaxService
*/
private extractData(res: Response) {
const body = res.json();
const out = body && body.hasOwnProperty('data') ? body.data : body;
return out;
}
/**
* Error handler
* Future Scope: Put into remote logging infra like into GCP stackdriver logger
* #param {(Response | any)} error
* #returns
*
* #memberOf AjaxService
*/
private handleError(error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''}${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
/**
* Init for RequestOptionsArgs
*
* #private
* #param {RequestOptionsArgs} [options]
* #returns
*
* #memberOf AjaxService
*/
private getBaseRequestOptions(options: RequestOptionsArgs = new BaseRequestOptions()) {
return options;
}
/**
* Set the default header
*
* #private
* #param {RequestOptionsArgs} options
* #returns
*
* #memberOf AjaxService
*/
private setHeaders(options: RequestOptionsArgs) {
if (!options.headers || !options.headers.has('Content-Type')) {
options.headers.append('Content-Type', 'application/json');
}
return options;
}
}
From what i've read this has something to do with the unit tests not executing asynchronously, hence, not waiting for the error response. However, I have no idea why this is the case here since I have used the async() utility function in the beforeEach method
You need to use it in the test case (the it). What async does is create an test zone that waits for all async tasks to complete before completing the test (or test area, e.g. beforeEach).
So the async in the beforeEach is only waiting for the async tasks to complete in the method before exiting it. But the it also needs that same thing.
it('should log an error to the console on error', async(() => {
}))
UPDATE
Aside from the missing async, there seems to be a bug with the MockConnection. If you look at the mockRespond, it always calls next, not taking into consideration the status code
mockRespond(res: Response) {
if (this.readyState === ReadyState.Done || this.readyState === ReadyState.Cancelled) {
throw new Error('Connection has already been resolved');
}
this.readyState = ReadyState.Done;
this.response.next(res);
this.response.complete();
}
They have a mockError(Error) method, which is what calls error
mockError(err?: Error) {
// Matches ResourceLoader semantics
this.readyState = ReadyState.Done;
this.response.error(err);
}
but this does not call allow you to pass a Response. This is inconsistent with how the real XHRConnection works, which checks for the status, and sends the Response either through the next or error, but is the same Response
response.ok = isSuccess(status);
if (response.ok) {
responseObserver.next(response);
// TODO(gdi2290): defer complete if array buffer until done
responseObserver.complete();
return;
}
responseObserver.error(response);
Sounds like a bug to me. Something you should probably report. They should allow you to either send the Response in the mockError or do the same check in the mockRespond that they do in the XHRConnection.
Updated (by OP) SetupConnections()
Current solution
function setupConnections(backend: MockBackend, options: any): any {
backend.connections.subscribe((connection: MockConnection) => {
const responseOptions: any = new ResponseOptions(options);
const response: any = new Response(responseOptions);
// Have to check the response status here and return the appropriate mock
// See issue: https://github.com/angular/angular/issues/13690
if (responseOptions.status >= 200 && responseOptions.status <= 299)
connection.mockRespond(response);
else
connection.mockError(response);
});
}
I use unit testing with karma with angular 2. I added Router in my service and but when I write test case for that it through me following error.
Error: Can't resolve all parameters for Router: (?, ?, ?, ?, ?, ?, ?, ?).
here is code
spec.ts
import 'rxjs/add/operator/map';
import { Http, BaseRequestOptions, Response, ResponseOptions,ConnectionBackend } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { GlobalUtils } from './global.utils';
import { Router} from '#angular/router';
import { TestBed, inject } from '#angular/core/testing';
describe('Global Utils : Meta urls API', () => {
let emptyMetaUrl = {};
let metaUrl = {
LOGIN: '/operator/login',
META_API: '/meta-api'
};
beforeEach(()=>TestBed.configureTestingModule({
providers: [
Router,
GlobalUtils,
BaseRequestOptions,
MockBackend,
{
provide: Http,
useFactory: function (backend:ConnectionBackend, defaultOptions:BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
]
}));
let subject:GlobalUtils = null;
let backend:MockBackend = null;
let http:Http = null;
beforeEach(inject([GlobalUtils, MockBackend, Http], (_globalUtils:GlobalUtils, mockBackend:MockBackend,_http:Http) => {
subject = _globalUtils;
backend = mockBackend;
http = _http;
}));
it('Should have get Meta urls', (done) => {
backend.connections.subscribe((connection:MockConnection)=> {
let options = new ResponseOptions({
body: JSON.stringify(metaUrl)
});
connection.mockRespond(new Response(options));
});
http
.get(subject.metaUrl)
.subscribe((response) => {
subject.getMetaUrls(); // in this routing is use
expect(GlobalUtils.saveUrl).toEqual(metaUrl);
done();
});
});
});
I used Routing in this service to navigate path.
service.ts
import {Injectable} from '#angular/core';
import { Router} from '#angular/router';
import {Http, Headers,Response} from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class GlobalUtils {
static saveUrl = { };
head:Headers = new Headers();
hostUrl:string = 'http://192.168.0.103:8000/';
metaUrl:string = 'urls-metadata/';
// constructor declare
constructor(public _http:Http,public _router:Router) {
this.head.append('Content-Type', 'application/json');
}
/*
* this function is used for Http GET Request
* */
urlGet(url:string) {
let headers = this.head;
return this._http.get(this.hostUrl + url, {headers: headers})
.map(res => res.json())
.map((res) => {
return res;
});
}
/*
* this function is used to GET all API url
* */
getMetaUrls():any{
let url = this.metaUrl;
this.urlGet(url).subscribe(
(result) => {
console.log('result = '+JSON.stringify(result));
if (result) {
GlobalUtils.saveUrl = result;
console.log('get Meta urls response = '+ result.status)
}
},
(error)=> {
this._router.navigate(['page-error']);
console.log('get Meta urls error = '+ error.status + " data =" +JSON.stringify(error))
},
()=>{console.log('get Meta url is successfully')}
);
}
}
when I run karma start this test case failed with this error.
✖ Should have get Meta urls
Chrome 52.0.2743 (Linux 0.0.0)
Error: Can't resolve all parameters for Router: (?, ?, ?, ?, ?, ?, ?, ?).
at CompileMetadataResolver.getDependenciesMetadata (webpack:///~/#angular/compiler/bundles/compiler.umd.js:14404:0 <- config/spec-bundle.js:53297:22)
at CompileMetadataResolver.getTypeMetadata (webpack:///~/#angular/compiler/bundles/compiler.umd.js:14301:0 <- config/spec-bundle.js:53194:29)
at webpack:///~/#angular/compiler/bundles/compiler.umd.js:14448:0 <- config/spec-bundle.js:53341:44
at Array.forEach (native)
at CompileMetadataResolver.getProvidersMetadata (webpack:///~/#angular/compiler/bundles/compiler.umd.js:14428:0 <- config/spec-bundle.js:53321:22)
at CompileMetadataResolver.getNgModuleMetadata (webpack:///~/#angular/compiler/bundles/compiler.umd.js:14181:0 <- config/spec-bundle.js:53074:61)
at RuntimeCompiler._compileComponents (webpack:///~/#angular/compiler/bundles/compiler.umd.js:16803:0 <- config/spec-bundle.js:55696:50)
at RuntimeCompiler._compileModuleAndAllComponents (webpack:///~/#angular/compiler/bundles/compiler.umd.js:16747:0 <- config/spec-bundle.js:55640:40)
at RuntimeCompiler.compileModuleAndAllComponentsSync (webpack:///~/#angular/compiler/bundles/compiler.umd.js:16735:0 <- config/spec-bundle.js:55628:24)
at TestingCompilerImpl.compileModuleAndAllComponentsSync (webpack:///~/#angular/compiler/bundles/compiler-testing.umd.js:758:0 <- config/spec-bundle.js:38833:36)
If you don't care to do any actual routing in the test, you can just create a mock for it
let mockRouter = {
navigate: jasmine.createSpy('navigate')
}
TestBed.configureTestingModule({
providers: [
{ provide: Router, useValue: mockRouter }
]
})
Then in your test maybe you just want to make sure that the navigate method is called. You can do
expect(mockRouter.navigate).toHaveBeenCalledWith(['/router']);
If you do want to test real navigation, then you should use the RouterTestingModule to add all the router providers and directives.
import { RouterTestingModule } from '#angular/router/testing';
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([{path: '', component: BlankCmp}, {path: 'simple', component: SimpleCmp}])
]
})
I have found many examples online of unit testing Angular 2 with a http call. However when I make my own test and run it Jasmine it gives me an error:
http Http .request() should accept a fully-qualified request as its only parameter
Failed: Cannot read property 'getXHR' of null
import {
afterEach,
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
injectAsync,
it,
xit
} from "angular2/testing";
import {Injector, provide} from "angular2/core";
import {MockBackend, MockConnection} from "angular2/src/http/backends/mock_backend";
import {
BaseRequestOptions,
ConnectionBackend,
Request,
RequestMethod,
RequestOptions,
Response,
ResponseOptions,
URLSearchParams,
JSONP_PROVIDERS,
HTTP_PROVIDERS,
XHRBackend,
JSONPBackend,
Http,
Jsonp
} from "angular2/http";
import {Observable} from "rxjs/Observable";
import {Subject} from "rxjs/Subject";
describe("http", () => {
let url = "http://foo.bar";
let http: Http;
let injector: Injector;
let backend: MockBackend;
let baseResponse;
let jsonp: Jsonp;
beforeEach(() => {
injector = Injector.resolveAndCreate([
BaseRequestOptions,
MockBackend,
provide(
Http,
{
useFactory: function(backend: ConnectionBackend, defaultOptions: BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
}),
provide(
Jsonp,
{
useFactory: function(backend: ConnectionBackend, defaultOptions: BaseRequestOptions) {
return new Jsonp(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
})
]);
http = injector.get(Http);
jsonp = injector.get(Jsonp);
backend = injector.get(MockBackend);
baseResponse = new Response(new ResponseOptions({body: "base response"}));
});
afterEach(() => backend.verifyNoPendingRequests());
describe("Http", () => {
describe(".request()", () => {
it("should return an Observable",
() => { expect(http.request(url)).toBeAnInstanceOf(Observable); });
it("should accept a fully-qualified request as its only parameter",
inject([injectAsync], (async) => {
backend.connections.subscribe(c => {
expect(c.request.url).toBe("https://google.com");
c.mockRespond(new Response(new ResponseOptions({body: "Thank you"})));
async.done();
});
http.request(new Request(new RequestOptions({url: "https://google.com"})))
.subscribe((res) => {});
}));
});
});
});
Any thoughts what I am doing wrong here?
You currently (beta.1) need to set the BrowserDomAdapter before running a unit test in Angular 2.
Using karma-test.shim.js
That can be done by adding karma-test.shim.js to your project, which contains the appropriate initialization:
System.import('angular2/src/platform/browser/browser_adapter')
.then(function(browser_adapter) {
browser_adapter.BrowserDomAdapter.makeCurrent();
})
Note that this should be done for you if you are using angular-cli.
Directly in the unit-test
Or directly in the unit test, by adding an import:
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
And later setting the DOM Adapter
BrowserDomAdapter.makeCurrent();