I new in Angular 2 and i'm trying create an App with JWT. So, to do this I follow the post http://www.adonespitogo.com/articles/angular-2-extending-http-provider/.
But i'm a issue, the request method is never call, after login i have to refresh the page to send the token.
Here my classes
http.service.ts
import { Injectable } from '#angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('auth_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
options.headers.append('Content-Type', 'application/json');
super(backend, options);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('auth_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
options.headers.append('Content-Type', 'application/json');
} else {
// we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
url.headers.append('Content-Type', 'application/json');
}
return super.request(url, options)
.catch(this.catchAuthError(this));
}
private catchAuthError (self: HttpService) {
// we have to pass HttpService's own instance here as `self`
return (res: Response) => {
console.log(res);
if (res.status === 401 || res.status === 403) {
// if not authenticated
console.log(res);
}
return Observable.throw(res);
};
}
}
app.module.ts
providers: [{
provide: HttpService,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new HttpService(backend, options);
},
deps: [XHRBackend, RequestOptions]
}, LoggedInGuard, UserService],
picture.service.ts
#Injectable()
export class PictureService {
url: string = 'v1/pictures';
constructor(private http: HttpService) { }
list(): Observable<PictureComponent[]> {
return this.http
.get(this.url)
.map(res => res.json());
}
}
Component to consume picture.service.ts
#Component({
moduleId: module.id,
selector: 'picture-list',
templateUrl: './pictureList.component.html'
})
export class ListagemComponent {
pictures: PictureComponent[] = [];
service: PictureService;
msg: String = '';
constructor(service: PictureService){
this.service = service;
this.service
.list()
.subscribe(pictures => {
this.pictures = pictures;
}, err => console.log(err));
}
}
thanks for help
Did you provide picture.service in your module? If not, you should provide it in your #component or in your module if you want it globally.
I extend XHRBackend
import { Injectable } from '#angular/core';
import { Request, XHRBackend, BrowserXhr, ResponseOptions, XSRFStrategy, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class ExtendedXHRBackend extends XHRBackend {
constructor(browserXhr: BrowserXhr, baseResponseOptions: ResponseOptions, xsrfStrategy: XSRFStrategy) {
super(browserXhr, baseResponseOptions, xsrfStrategy);
}
createConnection(request: Request) {
let token = localStorage.getItem('token');
request.headers.set('x-access-token', `${token}`);
request.headers.set('Content-Type', 'application/json');
let xhrConnection = super.createConnection(request);
xhrConnection.response = xhrConnection.response.catch((error: Response) => {
if (error.status === 401 || error.status === 403) {
console.log('access not alowed');
localStorage.removeItem('token');
}
return Observable.throw(error);
});
return xhrConnection;
}
}
and use on app module
providers: [{ provide: XHRBackend, useClass: ExtendedXHRBackend }]
After this I resolve the issue and works preety well
Related
Been trying to fetch products from WooCommerce REST API now forever and my brain is bleeding :'( I followed all instructions on woocommerce and github/woocommerce and I can't for my life get anything in Postman with Basic Auth:
But when I select Auth 1.0 - I get all products:
But then if I take the Auth 1.0 generated URL and put it in my browser:
..Instructions under Authentication over HTTP (here) describes the parameters which are generated in URL automatically when i select Auth 1.0 in Postman - but how am I gonna generate those in my React component?
const APP_URL = 'http://0.0.0.0:80'
const CONSUMER_KEY = 'ck_xxxx'
const CONSUMER_SECRET = 'cs_xxxx'
const PRODUCT_URL = `${APP_URL}/wp-json/wc/v2/products?consumer_key=${CONSUMER_KEY}&consumer_secret=${CONSUMER_SECRET}`
fetch(PRODUCT_URL).then(res => {
if(res.status === 200){
return json
} else {
console.log("ERROR RESPONSE STATUS: " + status);
}
}).then( json => {
console.log(json)
})
})
So thankful for all input!
I think this problem may be solved by below code using "Interceptor" concept...
import {
Injectable,
// Injector
} from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '#angular/common/http';
// import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
// import { AuthService } from './auth.service';
import { config } from '../config/config';
#Injectable()
export class AppInterceptor implements HttpInterceptor {
constructor(
// private injector: Injector,
// private router: Router
) { }
private includeWooAuth(url) {
const wooAuth = `consumer_key=${config.consumerKey}&consumer_secret=${config.secretKey}`;
const hasQuery = url.includes('?');
let return_url = '';
if (hasQuery) {
return_url = wooAuth;
} else {
return_url = '?' + wooAuth;
}
return return_url;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const auth = this.injector.get(AuthService);
const authRequest = request.clone({
setHeaders: {
// Authorization: `Bearer ${auth.getToken()}`
},
url: `${config.basePath}/${request.url}${this.includeWooAuth(request.url)}`
});
return next.handle(authRequest)
.catch(err => {
if (err instanceof HttpErrorResponse && err.status === 0) {
console.log('Check Your Internet Connection And Try again Later');
} else if (err instanceof HttpErrorResponse && err.status === 401) {
// auth.setToken(null);
// this.router.navigate(['/', 'login']);
}
return Observable.throw(err);
});
}
}
This code will be kept into http.interceptor.ts. Obviously, you should initialize the consumer key and other details of woocommerce API into a constant variable. After that you create a service for show the list of the product like this:
retriveProducts(query: ProductQuery = {}): Observable<RetriveProductsResponse> {
return this.httpClient.get(`products`, {params: this.wooHelper.includeQuery(query), observe: 'response'})
.pipe(
map(value => this.wooHelper.includeResponseHeader(value)),
catchError(err => this.wooHelper.handleError(err)));
}
And call this service to the product.ts file like this:
getProducts() {
this.woocommerceProductsService.retriveProducts()
.subscribe(productResponse => {
this.products = productResponse.products;
}, error => this.errorMessage = <any>error);
}
I have used these above code into my project. I think it will help you.
I'm getting the following error: Uncaught TypeError: result.subscribe is not a function
Here is also a screenshot of the error:
But I did tried to catch the error. Below you can see my code.
login.component.ts:
import { Component } from '#angular/core';
import { UserService } from '../../services/user.service';
import { User } from '../../models/user';
import {ToasterContainerComponent, ToasterService, ToasterConfig} from 'angular2-toaster/angular2-toaster';
#Component({
moduleId: module.id,
selector: 'login',
directives: [ToasterContainerComponent],
templateUrl: 'login.component.html',
providers: [UserService, ToasterService]
})
export class LoginComponent {
user: User = new User();
loginRes: String;
private toasterService: ToasterService;
public toasterconfig: ToasterConfig = new ToasterConfig({
showCloseButton: true,
tapToDismiss: false,
timeout: 0
});
constructor(private _userService: UserService, toasterService: ToasterService) {
this.toasterService = toasterService;
}
data = {};
onSubmit() {
this._userService.login(this.user)
.subscribe(
res => {
console.log("res onSubmit");
console.log(res);
},
function(error) { console.log("Error happened" + error)}
);
}
}
user.service.ts:
import { Injectable } from '#angular/core';
import { Headers, RequestOptions, Http, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { User } from '../models/user';
#Injectable()
export class UserService {
private loginUrl: string;
constructor(private _http: Http) {
}
login(user: User) {
this.loginUrl = "http://localhost:3000/api/auth/login";
let data = { "username": user.username, "password": user.password };
let body = JSON.stringify(data);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this._http.post(this.loginUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: any) {
console.log('Yup an error occurred', error);
return error.message || error;
}
}
As you can see I have tried to catch the error in the login() method in user.service.ts. Anyone that maybe knows how I can
solve this?
Your handleError() function needs to return an Observable
If you look at the HTTP Client Angular 2 docs there is an example, but for your specific case
Replace
private handleError(error: any) {
console.log('Yup an error occurred', error);
return error.message || error;
}
with
private handleError(error: any) {
console.log('Yup an error occurred', error);
return Observable.throw(error.message || error);
}
I'trying to use the HTTP service in Angular2 and i have some concerns.
I'm taking meteo datas from openweather API and I just want to put it inside a typeScript variable (meteo: {}) and use it as i want in my template.
Here are my .ts files:
meteo.service.ts
import {Injectable} from "angular2/core";
import {Http, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";
import {MeteoComponent} from "../widgets/meteo/meteo.component";
import {Meteo} from "../widgets/meteo/meteo";
#Injectable()
export class MeteoService {
constructor(private http: Http) {}
// Nom de la ville sans accent
private _ville = 'Montreal';
// Initiales du pays
private _country = 'ca';
// Units (metric/imperial)
private _units = 'metric';
// API KEY
private _APPID = 'ewfw54f5646';
// url to get data
private _meteoUrl = 'http://api.openweathermap.org/data/2.5/weather?q='+this._ville+','+this._country+'&units='+this._units+'&APPID='+this._APPID;
getMeteo (): Observable<Meteo> {
return this.http.get(this._meteoUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || { };
}
private handleError(error: any) {
let errMsg = error.message || 'server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
}
meteo.component.ts
import {Component, OnInit, OnChanges, AfterContentInit} from "angular2/core";
import {MeteoService} from "../../services/meteo.service";
import {Meteo} from "./meteo";
#Component({
selector: 'meteo',
templateUrl: 'dev/widgets/meteo/meteo.component.html',
providers: [MeteoService]
})
export class MeteoComponent implements OnInit {
errorMessage: string;
meteo: Meteo;
// We inject the service into the constructor
constructor (private _meteoService: MeteoService) {}
// Instantiate data in the ngOnInit function to keep the constructor simple
ngOnInit() {
this.getMeteo();
}
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo = data,
error => this.errorMessage = <any>error);
}
}
meteo.ts
export class Meteo {
data: {};
}
and meteo.component.html
<span class="meteo">{{meteo | json}}°C</span>
Actually the result is the entire json object:
{
"coord": {
"lon":-73.59,
"lat":45.51
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04d"
}
],
"base":"cmc stations",
"main":{
"temp":3.96,
"pressure":1020,
"humidity":32,
"temp_min":2,
"temp_max":6.67
},
"wind":{
"speed":2.1
},
"clouds":{
"all":75
},
"dt":1461594860,
"sys":{
"type":1,
"id":3829,
"message":0.004,
"country":"CA",
"sunrise":1461577807,
"sunset":1461628497
},
"id":6077243,
"name":"Montreal",
"cod":200
}
And I would like to display just the temp field.
If you have any idea guys it's welcomed!
Thanks a lot.
You could leverage the Elvis operator since your data are loaded asynchronously:
<span class="meteo">{{meteo?.main.temp | json}}°C</span>
Try setting the data on this.meteo.data
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo.data = data,
error => this.errorMessage = <any>error);
}
and then displaying it with
<span class="meteo">{{meteo.data.main.temp}}°C</span>
Part of my code:
import {Injectable} from 'angular2/core';
import {Http, Headers, Request, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class myClass {
constructor(protected http: Http) {}
public myMethod() {
let request = new Request({
method: "GET",
url: "http://my_url"
});
return this.http.request(request)
.map(res => res.json())
.catch(this.handleError); // Trouble line.
// Without this line code works perfectly.
}
public handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
myMethod() produces exception in console of browser:
ORIGINAL EXCEPTION: TypeError: this.http.request(...).map(...).catch is not a function
Perhaps you can try adding this in your imports:
import 'rxjs/add/operator/catch';
You can also do:
return this.http.request(request)
.map(res => res.json())
.subscribe(
data => console.log(data),
err => console.log(err),
() => console.log('yay')
);
Per comments:
EXCEPTION: TypeError: Observable_1.Observable.throw is not a function
Similarly, for that, you can use:
import 'rxjs/add/observable/throw';
New service updated to use the HttpClientModule and RxJS v5.5.x:
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { catchError, tap } from 'rxjs/operators';
import { SomeClassOrInterface} from './interfaces';
import 'rxjs/add/observable/throw';
#Injectable()
export class MyService {
url = 'http://my_url';
constructor(private _http:HttpClient) {}
private handleError(operation: String) {
return (err: any) => {
let errMsg = `error in ${operation}() retrieving ${this.url}`;
console.log(`${errMsg}:`, err)
if(err instanceof HttpErrorResponse) {
// you could extract more info about the error if you want, e.g.:
console.log(`status: ${err.status}, ${err.statusText}`);
// errMsg = ...
}
return Observable.throw(errMsg);
}
}
// public API
public getData() : Observable<SomeClassOrInterface> {
// HttpClient.get() returns the body of the response as an untyped JSON object.
// We specify the type as SomeClassOrInterfaceto get a typed result.
return this._http.get<SomeClassOrInterface>(this.url)
.pipe(
tap(data => console.log('server data:', data)),
catchError(this.handleError('getData'))
);
}
Old service, which uses the deprecated HttpModule:
import {Injectable} from 'angular2/core';
import {Http, Response, Request} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
//import 'rxjs/Rx'; // use this line if you want to be lazy, otherwise:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do'; // debug
import 'rxjs/add/operator/catch';
#Injectable()
export class MyService {
constructor(private _http:Http) {}
private _serverError(err: any) {
console.log('sever error:', err); // debug
if(err instanceof Response) {
return Observable.throw(err.json().error || 'backend server error');
// if you're using lite-server, use the following line
// instead of the line above:
//return Observable.throw(err.text() || 'backend server error');
}
return Observable.throw(err || 'backend server error');
}
private _request = new Request({
method: "GET",
// change url to "./data/data.junk" to generate an error
url: "./data/data.json"
});
// public API
public getData() {
return this._http.request(this._request)
// modify file data.json to contain invalid JSON to have .json() raise an error
.map(res => res.json()) // could raise an error if invalid JSON
.do(data => console.log('server data:', data)) // debug
.catch(this._serverError);
}
}
I use .do() (now .tap()) for debugging.
When there is a server error, the body of the Response object I get from the server I'm using (lite-server) contains just text, hence the reason I use err.text() above rather than err.json().error. You may need to adjust that line for your server.
If res.json() raises an error because it could not parse the JSON data, _serverError will not get a Response object, hence the reason for the instanceof check.
In this plunker, change url to ./data/data.junk to generate an error.
Users of either service should have code that can handle the error:
#Component({
selector: 'my-app',
template: '<div>{{data}}</div>
<div>{{errorMsg}}</div>`
})
export class AppComponent {
errorMsg: string;
constructor(private _myService: MyService ) {}
ngOnInit() {
this._myService.getData()
.subscribe(
data => this.data = data,
err => this.errorMsg = <any>err
);
}
}
There are several ways to do this. Both are very simple. Each of the examples works great. You can copy it into your project and test it.
The first method is preferable, the second is a bit outdated, but so far it works too.
1) Solution 1
// File - app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule } from '#angular/common/http';
import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ProductModule } from './product.module';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [ProductService, ProductModule],
bootstrap: [AppComponent]
})
export class AppModule { }
// File - product.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
// Importing rxjs
import 'rxjs/Rx';
import { Observable } from 'rxjs/Rx';
import { catchError, tap } from 'rxjs/operators'; // Important! Be sure to connect operators
// There may be your any object. For example, we will have a product object
import { ProductModule } from './product.module';
#Injectable()
export class ProductService{
// Initialize the properties.
constructor(private http: HttpClient, private product: ProductModule){}
// If there are no errors, then the object will be returned with the product data.
// And if there are errors, we will get into catchError and catch them.
getProducts(): Observable<ProductModule[]>{
const url = 'YOUR URL HERE';
return this.http.get<ProductModule[]>(url).pipe(
tap((data: any) => {
console.log(data);
}),
catchError((err) => {
throw 'Error in source. Details: ' + err; // Use console.log(err) for detail
})
);
}
}
2) Solution 2. It is old way but still works.
// File - app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ProductModule } from './product.module';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpModule
],
providers: [ProductService, ProductModule],
bootstrap: [AppComponent]
})
export class AppModule { }
// File - product.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
// Importing rxjs
import 'rxjs/Rx';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ProductService{
// Initialize the properties.
constructor(private http: Http){}
// If there are no errors, then the object will be returned with the product data.
// And if there are errors, we will to into catch section and catch error.
getProducts(){
const url = '';
return this.http.get(url).map(
(response: Response) => {
const data = response.json();
console.log(data);
return data;
}
).catch(
(error: Response) => {
console.log(error);
return Observable.throw(error);
}
);
}
}
The RxJS functions need to be specifically imported. An easy way to do this is to import all of its features with import * as Rx from "rxjs/Rx"
Then make sure to access the Observable class as Rx.Observable.
in the latest version of angular4 use
import { Observable } from 'rxjs/Rx'
it will import all the required things.
First: I'm aware that Angular2 is in alpha and changing frequently.
I'm working with Angular2. There is an injectable service with http dependency that I'd like to test using a mock backend. The service works when the app starts but I'm having no luck writing the test and getting the mock backend to respond. Any insight, is there something obvious in the test setup or implementation that I'm missing?
service/core.ts:
import { Injectable } from 'angular2/angular2';
import { Http } from 'angular2/http';
#Injectable()
export class CoreService {
constructor(public http:Http) {}
getStatus() {
return this.http.get('/api/status')
.toRx()
.map(res => res.json());
}
}
service/core_spec.ts:
import {
AsyncTestCompleter,
TestComponentBuilder,
By,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit
} from 'angular2/test';
import { MockBackend, MockConnection, BaseRequestOptions, Http, Response } from 'angular2/http';
import { Injector, bind } from 'angular2/angular2';
import { ObservableWrapper } from 'angular2/src/core/facade/async'
import { CoreService } from 'public/services/core'
export function main() {
describe('public/services/core', () => {
let backend: MockBackend;
let response: Response;
let coreService: CoreService;
let injector: Injector;
afterEach(() => backend.verifyNoPendingRequests());
it('should get status', inject([AsyncTestCompleter], (async) => {
injector = Injector.resolveAndCreate([
BaseRequestOptions,
MockBackend,
bind(Http).toFactory((backend, options) => {
return new Http(backend, options)
}, [MockBackend, BaseRequestOptions]),
bind(CoreService).toFactory((http) => {
return new CoreService(http);
}, [Http])
]);
backend = injector.get(MockBackend);
coreService = injector.get(CoreService);
response = new Response('foo');
ObservableWrapper.subscribe<MockConnection>(backend.connections, c => {
expect(c.request.url).toBe('/api/status');
c.mockRespond(response);
});
// attempt #1: fails because res argument is undefined
coreService.getStatus().subscribe(res => {
expect(res).toBe('');
async.done();
});
// attempt #2: fails because emitter.observer is not a function
ObservableWrapper.subscribe(coreService.getStatus(), res => {
expect(res).toBe('');
async.done();
});
}));
});
}
Related:
https://github.com/angular/angular/issues/3502
https://github.com/angular/angular/issues/3530
I just found this topic while looking for testing tips but I can't see a direct answer to that so...
This one is based on Angular RC.1
Testing service with Mock Backend
Let's say your service is:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
#Injectable()
export class CoreService {
constructor(private http: Http) {}
getStatus() {
return this.http.get('/api/status');
}
}
Test to the service above will look like this:
import {
beforeEach,
beforeEachProviders,
describe,
expect,
inject,
it,
} from '#angular/core/testing';
import { provide } from '#angular/core';
import { BaseRequestOptions, Response, ResponseOptions } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
describe('Http', () => {
beforeEachProviders(() => [
CoreService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
})
]);
beforeEach(inject([MockBackend], (backend: MockBackend) => {
const baseResponse = new Response(new ResponseOptions({ body: 'status' }));
backend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse));
}));
it('should return response when subscribed to getStatus',
inject([CoreService], (coreService: CoreService) => {
coreService.getStatus().subscribe((res: Response) => {
expect(res.text()).toBe('status');
});
})
);
})
What you really have to look at there is to have proper mocking in beforeEachProviders. Test itself is quite simple and ends up with subscribing to the service method.
Note: Don't forget to set base providers first:
import { setBaseTestProviders } from '#angular/core/testing';
import {
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS,
TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
} from '#angular/platform-browser-dynamic/testing';
setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
Since asking this question we did upgrade to Angular2 RC 1. Our imports look like Wojciech Kwiatek's (thank you for your answer!) but our testing strategy is slightly different. We wanted to assert on the request as well as the response. Instead of using beforeEachProviders(), we used beforeEach() where we create our own injector and save a reference to the service-under-test and mock backend. This allows us to assert on the request and manage the response inside the test, and it lets us use the verifyNoPendingRequests() method after each test.
describe('core-service', () => {
let service: CoreService;
let backend: MockBackend;
beforeEach(() => {
injector = ReflectiveInjector.resolveAndCreate(<any> [
CoreService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (mockBackend, defaultOptions) => new Http(mockBackend, defaultOptions),
deps: [MockBackend, BaseRequestOptions]
})
]);
service = <CoreService> injector.get(CoreService);
backend = <MockBackend> injector.get(MockBackend);
});
afterEach(() => backend.verifyNoPendingRequests());
it('should get status', () => {
backend.connections.subscribe((c: MockConnection) => {
expect(c.request.url).toEqual('api/status');
c.mockRespond(new Response(new ResponseOptions({ body: 'all is well' })));
});
service.getStatus().subscribe((status) => {
expect(status).toEqual('all is well');
});
}));
});
Edit: Plunker updated to RC2.
https://plnkr.co/edit/nlvUZVhKEr8d2mz8KQah?p=preview