Expected one matching request for criteria - javascript

I am trying to follow the angular guide to testing services using the new HTTP Client. I am getting the following error, Expected one matching request for criteria "Match method: GET, URL: http://localhost:8080/services/shift/2016-12-01", found none. I have put my code below, not too sure where I'm going wrong
Unit Test
import { HttpTestingController, HttpClientTestingModule } from '#angular/common/http/testing';
import { HttpClient, HttpHandler } from '#angular/common/http';
import { TestBed } from '#angular/core/testing';
import { ShiftService } from './shift.service';
let service: ShiftService;
let backend: HttpTestingController;
describe('ShiftService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ShiftService,
HttpClient,
HttpHandler
],
imports: [HttpClientTestingModule]
});
service = TestBed.get(ShiftService);
backend = TestBed.get(HttpTestingController);
});
afterEach(() => {
backend.verify();
});
describe('When the getShift method is invoked', () => {
it('should make a GET request to the services/shift endpoint', async() => {
service.getShift().subscribe();
backend.expectOne({
url: 'http://localhost:8080/services/shift/2016-12-01',
method: 'GET'
});
});
});
});
Service
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class ShiftService {
constructor(private http: HttpClient) { }
public getShift = () => {
return this.http.get('http://localhost:8080/services/shift/2016-12-01');
}
}
I have made sure to subscribe to my getShift() method and I am using the HTTPTestingController. I have also tried the other overloads of the HttpTestingController and no luck :/ Thank you for any help in advance!

use describe as below
describe('When the getShift method is invoked', () => {
it('should make a GET request to the services/shift endpoint', async() => {
const path = '/testPath';
service.get(path).subscribe(response => {
expect(response).toBeTruthy();
});
const httpRequest = httpMock.expectOne(
(req: HttpRequest<any>) => req.urlWithParams === path);
// write your expect criteria here....
});
});

Related

How to get status code in angular observable

I have services below that I'd like to get status code and handle if statements in it but so far I couldn't figure it out
import { Injectable } from '#angular/core';
import { EnvService } from './env.service';
import { tap } from 'rxjs/operators';
import { Observable, from } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
import { Plugins } from '#capacitor/core';
const { Storage } = Plugins;
#Injectable({
providedIn: 'root'
})
export class InvoicesServiceService {
token: any;
constructor(
private env: EnvService,
private http: HttpClient,
private nativeStorage: NativeStorage
) {
Storage.get({ key: 'token' }).then((token: any) => {
this.token = JSON.parse(token.value)
}).catch(error => console.error(error));
}
// All
getInvoices(): Observable<any> {
const tokenPromise =
this.token === undefined
? Storage.get({ key: 'token' })
: Promise.resolve(this.token);
return from(tokenPromise).pipe(
switchMap((token) => {
this.token = this.token;
const httpOptions = {
headers: new HttpHeaders({
Accept: 'application/json, text/plain',
'Content-Type': 'application/json',
Authorization: this.token.access_token,
}),
};
return this.http
.get(`${this.env.Dashboard}` + '/invoices', httpOptions)
.pipe(map((data) => data));
})
);
}
What I try to do is that if, status code is 403 redirect user to specific route other than that just return data.
any idea?
In component where you subscribe this service you can handle error
this.service
.getInvoices()
.subscribe((response) => {
// This is success
},
(error: HttpErrorResponse) => {
// Handle error
// Use if conditions to check error code, this depends on your api, how it sends error messages
});
Another way to handle in service itself.
return this.http
.get(`${this.env.Dashboard}` + '/invoices', httpOptions)
.pipe(map((data) => data))
.toPromise()
.then((response) => {
//Success
})
.catch((error: HttpErrorResponse) => {
// Handle error
});
Hope this helps.
The error is not always sent in the headers.
Sometimes the erros comes via HTML message, like when NGINX tells you someting before you even get to the backend:
<html>
<head><title>413 Request Entity Too Large</title></head>
<body>
<center><h1>413 Request Entity Too Large</h1></center>
<hr><center>nginx</center>
</body>
</html>
In these cases you should use if (error.includes('413 Request Entity Too Large')) {...}

Error: Expected no open requests, found 1 (Angular)

I am trying to create a test case for a service in angular6. The service has a bunch of different http request methods (get, put, post etc) and within them an API call is made which fetches the appropriate response. I'm trying to create the test cases where a mock http request is made and a response is returned. However, I have followed a Tutorial which apparently helps me do exactly what I want.
However, when I run the test case for the service it gives me the following error (I've censored the URL in GET for privacy purposes:
Error: Expected no open requests, found 1: GET https://staging.xxxxxxxxxx.co.uk/rest/v11_1/oauth2/token
at HttpClientTestingBackend.push../node_modules/#angular/common/fesm5/http/testing.js.HttpClientTestingBackend.verify (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/common/fesm5/http/testing.js:326:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/src/app/Services/adapter.service.spec.ts:22:13)
at TestBed.push../node_modules/#angular/core/fesm5/testing.js.TestBed.execute (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/fesm5/testing.js:1073:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/fesm5/testing.js:1224:29)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:288:1)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:387:1)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:138:1)
at runInTestZone (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:509:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:524:1)
I've tried browsing through This solution as well as This one, but to no avail.
Here is the code for my service:
import { Injectable } from '#angular/core';
import { environment } from '../../environments/environment';
import {
HttpHeaders,
HttpClient,
HttpParams,
} from '#angular/common/http';
import { Request, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { JwtService } from './jwt.service';
const API_URL = environment.api.host;
#Injectable({
providedIn: 'root'
})
export class AdapterService {
constructor(private http: HttpClient, private jwtService: JwtService) {}
private formatErrors(self: AdapterService) {
return (res: Response) => {
return Observable.throw(res);
};
}
private requestHeaders(path: string) {
let headers;
if (path !== 'oauth2/token') {
headers = new HttpHeaders({
'Accept': 'application/json',
'Oauth-Token': this.jwtService.getToken()
})
}
return headers;
}
get(path: string, params: HttpParams = new HttpParams()): Observable < any > {
let headers = this.requestHeaders(path);
return this.http.get(`${API_URL}${path}`, { headers })
.catch(catchError(this.formatErrors(this)));
}
put(path: string, body: Object = {}): Observable < any > {
return this.http.put(
`${API_URL}${path}`,
JSON.stringify(body),
).catch(catchError(this.formatErrors(this)));
}
post(path: string, body: Object = {}): Observable < any > {
return this.http.post(
`${API_URL}${path}`,
JSON.stringify(body),
).catch(catchError(this.formatErrors(this)));
}
delete(path): Observable < any > {
return this.http.delete(
`${API_URL}${path}`,
).catch(catchError(this.formatErrors(this)));
}
}
The Test Case:
import { TestBed, async, inject } from '#angular/core/testing';
import { HttpClientModule, HttpRequest, HttpParams } from '#angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { AdapterService } from './adapter.service';
describe('AdapterService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule,
HttpClientTestingModule
],
providers: [
AdapterService
]
});
});
afterEach(inject([HttpTestingController], (backend: HttpTestingController) => {
backend.verify();
}));
it('should send a valid get request for token', async(inject([AdapterService, HttpTestingController],
(service: AdapterService, backend: HttpTestingController) => {
service.get('oauth2/token').subscribe((next)=>{
expect(next).toBeDefined();
});
})));
// it('')
});
SOLVED I forgot to add an expectOne request for the API call within the test case:
backend.expectOne( API_URL + 'oauth2/token').flush(null, { status: 200, statusText:'Ok' });
A very naive observation, apologies for the inconvenience.

HTTP post request unit testing in Angular 4

THIS TEST IS WORKING NOW
I would like to test an HTTP request in Angular 2, but it is not working.
Here is the error message:
ERROR in .../loginservice.service.spec.ts (45,11): ';' expected.
ERROR in .../loginservice.service.spec.ts (45,12): ')' expected.
and here is the code:
It is a post request, and it working properly.
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class LoginService {
constructor(private http: Http) { }
postLoginDetails(loginDetails): Observable<Comment[]> {
const endpoint = 'http://localhost:8080/api/login';
const bodyString = loginDetails;
const headers = new Headers({ 'Content-Type': 'application/json'});
const options = new RequestOptions({headers: headers});
return this.http.post(endpoint, bodyString, options)
.map((res: Response) => res.json())
.catch((error: any) => Observable.throw(error.json().error || 'server error'));
}
}
and here is the test for it:
This is the test for the post request. I used different articles to write it and perhaps that is why it is not working.
import { TestBed, inject } from '#angular/core/testing';
import {
HttpModule,
Http,
XHRBackend,
ResponseOptions,
Response,
Headers
} from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { LoginService } from './loginservice.service';
describe('LoginService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
LoginService,
{ provide: XHRBackend, useClass: MockBackend },
]
});
});
describe('postLoginDetails()', () => {
it('should return an Observable<Comment[]> with ok status',
inject([LoginService, XHRBackend], (LoginService, MockBackend) => {
const mockResponse = {
status: 'ok',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRlc3RBZG1pbiIsImFkbWluIjp0cnVlfQ.nhC1EDI5xLGM4yZL2VMZyvHcbcWiXM2RVS7Y8Pt0Zuk'
}
const loginDetails = {
email: 'test#example.com',
password: '1234'
};
MockBackend.connections.subscribe((connection) => {
connection.mockRespond(new Response(new ResponseOptions({
body: JSON.stringify(mockResponse)
})));
});
LoginService.postLoginDetails(loginDetails).subscribe((mockResponse) => {
expect(mockResponse.status).toEqual('ok');
});
}));
});
});
There is mismatch } in while closing mockBackend.connections
describe('postLoginDetails()', () => {
it('should return an Observable<Comment[]>', inject([LoginService, XHRBackend], (loginService, mockBackend) => {
const mockResponse = {
status: 'ok',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRlc3RBZG1pbiIsImFkbWluIjp0cnVlfQ.nhC1EDI5xLGM4yZL2VMZyvHcbcWiXM2RVS7Y8Pt0Zuk'
};
mockBackend.connections.subscribe((connection) => {
const loginDetails = {
email: 'test#example.com',
password: '1234'
};
loginService.postLoginDetails(loginDetails).subscribe((userInfo) => {
expect(userInfo.length).toBe(2);
expect(userInfo.status).toEqual('ok');
});
}));
});
});
update:- fixed some syntax error.
Nice question.
Yes, this is definitely a false positive test...

Angular 2.0 - converting promise chaining to Observables

I am converting an NG 1.X service to NG 2.0.
My NG 1.X service has promise chaining (simplified):
dataService.search = function(searchExp) {
return this.getAccessToken()
.then(function(accesstoken) {
var url = $interpolate('https://my-api-url?q={{search}}&{{accesstoken}}')({search: searchExp, accesstoken: accesstoken});
return $http({
url: url,
method: 'GET',
cache: true
});
}).then(function(response) {
return response.data;
});
};
I want to convert search service to be an Angular 2.0 Service, using http and returning Observable. I prefer leaving the getAccessToken service untouched, as an NG 1.X service, which returns a promise.
I was thinking about using Observable.fromPromise on the old "promise" service.
How can I do it? How can I chain those two?
EDIT:
Just to clarify, I want it to be something like this:
dataService.search = function(searchExp) {
return this.getAccessToken()
.then(function(accesstoken) {
//Here I want to use:
// this.http.get(url).subscribe(() => ...)
});
};
You should make search method return Observable object. Something like this:
dataService.search = function(searchExp) {
var promise = new Promise((resolve, reject) => {
this.getAccessToken()
.then(accesstoken => {
return this.http.get('data.json')
.map(response => response.json())
.subscribe(data => resolve(data), err => reject(err))
})
});
return PromiseObservable.create(promise); // Observable.fromPromise(promise)
};
I converted #dfsq's Plunker to beta.0. map() doesn't seem to be available anymore without importing it (but we don't need it here).
import {Component, Injectable} from 'angular2/core';
import {HTTP_PROVIDERS, Http} from 'angular2/http';
import {PromiseObservable} from 'rxjs/observable/fromPromise';
#Injectable()
export class DataService {
constructor(private _http: Http, private _accessService: AccessService) {}
search(searchExp) {
var promise = new Promise((resolve, reject) => {
this._accessService.getAccessToken() // see Plunker for AccessService
.then(accessToken => {
return this._http.get('data.json') // use accessToken here
.subscribe(res => resolve(res.json()), err => reject(err));
});
});
return PromiseObservable.create(promise);
}
}
#Component({
selector: 'my-app',
providers: [HTTP_PROVIDERS, AccessService, DataService],
template: `<h2>Data loaded</h2><pre>{{data | json}}</pre>
`
})
export class AppComponent {
data: any;
constructor(private _dataService: DataService) {
console.clear();
}
ngOnInit() {
this._dataService.search('test')
.subscribe(res => {
this.data = res;
});
}
}
beta.0 Plunker

How to do a unit test for Http get MockBackend in Angular2?

How to do a unit test for Http get MockBackend in Angular2?
I'm having trouble testing my http unit test.
Every time I look at MockBackend it seems confusing, a lot of code and some imports never work.
I just want a very basic http get unit test
I'm using: typescript, angular2, jasmine and karma runner.
My actual code works fine.
Here is my code that I'm testing:
import {Injectable} from 'angular2/angular2';
import {HTTP_PROVIDERS, Http, Headers} from 'angular2/http';
#Injectable()
export class FirebaseService{
headers: Headers;
//Test issue seems to be here when I inject Http instance.
constructor(public http?: Http) {
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
}
//This is the method I'm testing.
public getSpotifyTracks = ():Promise<Object> =>{
return this.http
.get('https://api.spotify.com/v1/tracks/0eGsygTp906u18L0Oimnem', {headers:this.headers})
.map((response) => {
return response.json()
}).toPromise();
}
}
Here is my unit test for that code:
import {it, iit, describe, expect, inject, injectAsync, beforeEachProviders, fakeAsync, tick} from 'angular2/testing';
import {HTTP_PROVIDERS, Http, Headers} from 'angular2/http';
import {FirebaseService} from '../app/firebase-service';
describe('Firebase Service Calls', () => {
beforeEachProviders(()=> [Http, FirebaseService]);
//Issue seems to be here????
it('get all tracks from spotify', injectAsync([FirebaseService],(service) => {
return service.getSpotifyTracks().then((response) => {
expect(response.length).not.toBe(null);
});
}), 3000);
});
First import all modules :
import {it,describe,expect,inject,injectAsync,beforeEachProviders} from 'angular2/testing';
import {provide, Injector} from 'angular2/core';
import {MockBackend} from 'angular2/http/testing';
import {YourServiceToBeTested} from 'path/to/YourServiceToBeTested';
Next you need to declare the Mocked HttpBackend :
describe('Service with Http injected', () => {
beforeEachProviders(() => {
[
MockBackend,
BaseRequestOptions,
provide(
Http,
{
useFactory: (backend, defaultOptions) => {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
}),
YourServiceToBeTested
]
});
Finally on each test, you need to inject the mock & set the mocked value (ie the fake data returned by your service for this specific test)
it('should respect your expectation',
inject(
[YourServiceToBeTested, MockBackend],
(yourServiceToBeTested, mockBackend) => {
let response = 'Expected Response from HTTP service usually JSON format';
let responseOptions = new ResponseOptions({body: response});
mock.connections.subscribe(
c => c.mockRespond(new Response(responseOptions)));
var res = yourServiceToBeTested.ServiceMethodToBeTest(serviceParams);
expect(res).toEqual('your own expectation');
}));
While #f-del s answer gets the same result this is easier and uses Angulars DI better.
describe('Firebase Service Calls', () => {
beforeEachProviders(()=> [
HTTP_PROVIDERS,
MockBackend,
provide(XHRBackend, {useExisting: MockBackend})]);
This way, when Http is requested, and instance that uses MockBackend is provided.
In Angular 2.2.1 provide does not exist in core anymore , so we should do :
{
provide : Http,
deps : [ MockBackend, BaseRequestOptions ],
useFactory : ( backend : MockBackend, defaultOptions : BaseRequestOptions ) => {
return new Http( backend, defaultOptions );
}
}
To piggyback off of #Milad's response, I found a great tutorial on mocking http calls for Angular 2/4 unit tests.
searchService.service.ts
import {Injectable} from '#angular/core';
import {Jsonp} from '#angular/http';
import 'rxjs/add/operator/toPromise';
class SearchItem {
constructor(public name: string,
public artist: string,
public thumbnail: string,
public artistId: string) {
}
}
#Injectable()
export class SearchService {
apiRoot: string = 'https://itunes.apple.com/search';
results: SearchItem[];
constructor(private jsonp: Jsonp) {
this.results = [];
}
search(term: string) {
return new Promise((resolve, reject) => {
this.results = [];
let apiURL = `${this.apiRoot}?term=${term}&media=music&limit=20&callback=JSONP_CALLBACK`;
this.jsonp.request(apiURL)
.toPromise()
.then(
res => { // Success
this.results = res.json().results.map(item => {
console.log(item);
return new SearchItem(
item.trackName,
item.artistName,
item.artworkUrl60,
item.artistId
);
});
resolve(this.results);
},
msg => { // Error
reject(msg);
}
);
});
}
}
searchService.service.spec.ts
describe('Service: Search', () => {
let service: SearchService;
let backend: MockBackend;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [JsonpModule],
providers: [
SearchService,
MockBackend,
BaseRequestOptions,
{
provide: Jsonp,
useFactory: (backend, options) => new Jsonp(backend, options),
deps: [MockBackend, BaseRequestOptions]
}
]
});
backend = TestBed.get(MockBackend);
service = TestBed.get(SearchService);
});
});
it('search should return SearchItems', fakeAsync(() => {
let response = {
"resultCount": 1,
"results": [
{
"artistId": 78500,
"artistName": "U2",
"trackName": "Beautiful Day",
"artworkUrl60": "image.jpg",
}]
};
// When the request subscribes for results on a connection, return a fake response
backend.connections.subscribe(connection => {
connection.mockRespond(new Response(<ResponseOptions>{
body: JSON.stringify(response)
}));
});
// Perform a request and make sure we get the response we expect
service.search("U2");
tick();
expect(service.results.length).toBe(1);
expect(service.results[0].artist).toBe("U2");
expect(service.results[0].name).toBe("Beautiful Day");
expect(service.results[0].thumbnail).toBe("image.jpg");
expect(service.results[0].artistId).toBe(78500);
}));
Code and credit goes to Asim at CodeCraft.

Categories

Resources