Angular 2/4 Jasmine unit testing a service with json object structure - javascript

I have a service that I want to unit test. It has a current json array object in which when this observable is subscribed to, it works fine.
However, what am I doing wrong with my unit test?
Service code
getTrackerData(): (Observable<any>) {
return Observable.of(tracker)
.do(data => console.log('trackerdata', data))
.catch(this.handleError)
}
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
DATA
const tracker =
[{
"tracker": "2278474849", "task": 1, "agent": 1, "session": "2278474849-1-1"
}
]
Unit Test
/// <reference path="../../../../node_modules/#types/jasmine/index.d.ts" />
import { TestBed, fakeAsync, inject, tick, async, ComponentFixture, ComponentFixtureAutoDetect } from '#angular/core/testing';
import { MockBackend } from '#angular/http/testing'
import { Http, BaseRequestOptions, Response, ResponseOptions } from '#angular/http'
import { BrowserModule, By } from "#angular/platform-browser";
import { ReactiveFormsModule } from '#angular/forms';
import { Observable } from 'rxjs/Rx'
import { TrackerService } from './tracker.service'
describe('tracker service', () => {
let mockResponse, matchingItem, connection
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
TrackerService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend, defaultOptions) => new Http(backend, defaultOptions),
deps: [MockBackend, BaseRequestOptions]
},
]
});
const items =
[{
"tracker": "2278474849", "task": 1, "agent": 1, "session": "2278474849-1-1"
}
];
mockResponse = new Response(new ResponseOptions({ body: items, status: 200 }));
}));
describe('getTrackerData', () => {
//subscribe to connection and storing it later
it('should return all items', inject([TrackerService, MockBackend], (service: TrackerService, backend: MockBackend) => {
backend.connections.subscribe(connection => {
connection.mockRespond(mockResponse);
});
service.getTrackerData() //{ "tracker": "2278474849", "task": 1, "agent": 1, "session": "2278474849-1-1" }
.subscribe((items: any => {
expect(this).toBe(2);
}));
})
});
I seem to be getting errors in the describe section , Problem is that I'm not used to writing javascript/angular unit test, along with mixing up a few examples in the project I'm on coupled with online examples.
Seems like what I want/need to do should be way simple , but yet I imported so many dependencies that this is now just confusing.
Anyone know what I need to do?

I've been testing services by getting the service instance from the getTestBed function. Try changing your inner describe block to:
describe('getTrackerData', () => {
it('should return all items', (done) => {
let service: TrackerService;
let bed = getTestBed();
backend.connections.subscribe(connection => {
connection.mockRespond(mockResponse);
});
service = bed.get(TrackerService);
service.getTrackerData()
.subscribe((items: any => {
expect(this).toBe(2);
done();
}));
})
Make sure to import getTestBed from '#angular/core/testing'.
I'm also not sure you need any of these imports:
import { BrowserModule, By } from "#angular/platform-browser";
import { ReactiveFormsModule } from '#angular/forms';
import { Observable } from 'rxjs/Rx'

Related

How to testing a service in Angular 7

I want to test the data I receive from my service.
I read angular documentation and I followed observable example but I don't receive any data when I subscribed to the observable.
Console.log() isn't working inside the subscribe.
The service is working properly and get the right data in a real environment.
I tried to use async and doneFn but they didn't work both of them got time out.
Service File
export class BackService {
URL = '********'; // I removed url for security.
constructor(private httpClient: HttpClient) { }
getAllForms(): Observable<Array<object>> {
return this.httpClient.get<Array<object>>(`${this.URL}/allForms`);
}
getFormById(formId): Observable<Array<object>> {
return this.httpClient.get<Array<object>>(`${this.URL}/form/${formId}`);
}
}
Test Service File
import { TestBed } from '#angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { BackService } from './back.service';
describe('BackService', () => {
let httpMock: HttpTestingController;
let backService: BackService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [BackService]
});
backService = TestBed.get(BackService);
httpMock = TestBed.get(HttpTestingController);
});
it('should be created', () => {
expect(backService).toBeTruthy();
});
describe('Methods', () => {
describe('All Forms', () => {
it('should use GET method', () => {
backService.getAllForms().subscribe();
const req = httpMock.expectOne(`${backService.URL}/allForms`);
expect(req.request.method).toBe('GET');
});
it('should use the right url', () => {
backService.getAllForms().subscribe();
const req = httpMock.expectOne(`${backService.URL}/allForms`);
expect(req.request.url).toBe(`${backService.URL}/allForms`);
});
it('should return the right data', () => {
const mockData = [{'_id': 435345345, '_type': 'window'}]
backService.getAllForms().subscribe(data => {
expect(data).toEqual(mockData);
});
});
});
});
First 2 tests look ok, for third test to receive data that you can test on, you have to trigger that "httpMock" by calling its flush() method with necessary object you want your httpClient to return.
This should work for third test:
it('should return the right data', () => {
const mockData = [{'_id': 435345345, '_type': 'window'}]
backService.getAllForms().subscribe(data => {
expect(data).toEqual(mockData);
});
const req = httpMock.expectOne(`${backService.URL}/allForms`);
req.flush(mockData);
});
we do pact tests because you have to mock a lot which didn't feel real for us.
Example
import {PactWeb} from '#pact-foundation/pact-web';
describe('FooService', () => {
let provider: PactWeb;
beforeAll(async () => {
await provider.addInteraction({
state: 'I have an foo',
uponReceiving: 'a request to create an bar',
withRequest: {
method: 'POST',
path: '/url/bars',
body: {
foo: '123456',
bar: 'abcd'
}
},
willRespondWith: {
status: 200,
headers: {'Content-Type': 'application/json'},
body: {
foo: '123456',
bar: 'abcd'
}
}
});
});
it('should create one bar and respond with that bar', async () => {
const service: FooService = TestBed.get(FooService);
(service as any).apiBasePath = provider.mockService.baseUrl + 'url';
const result = await service.createBar({
foo: '123456',
bar: 'abcd'
}).toPromise();
expect(result.id).toEqual('some-random-uuid');
});
afterAll(function (done) {
provider.finalize()
.then(function () {
done();
}, function (err) {
done.fail(err);
});
});
});
I assume, that you have a service called "createBar" that you want to test.
State
Is just to know what you are doing, so it is the state of the provider. He has a foo. And uponReceiving of the request he should create a bar
withRequest
Shows how the request should look like
willRespondWith
Shows the response.
Can you try with MockBackend instead?
import { TestBed, inject } from "#angular/core/testing";
import {
HttpModule,
Http,
Response,
ResponseOptions,
XHRBackend
} from "#angular/http";
import { MockBackend } from "#angular/http/testing";
import { BackService } from "./back.service";
describe("BackService", () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [BackService, { provide: XHRBackend, useClass: MockBackend }]
});
});
describe("getAllForms()", () => {
it("should return an Observable<Array<Form>>", inject(
[BackService, XHRBackend],
(backService, mockBackend) => {
const mockResponse = {
data: [
{ id: 0, form: "Form 0" },
{ id: 1, form: "Form 1" },
{ id: 2, form: "Form 2" },
{ id: 3, form: "Form 3" }
]
};
mockBackend.connections.subscribe(connection => {
connection.mockRespond(
new Response(
new ResponseOptions({
body: JSON.stringify(mockResponse)
})
)
);
});
backService.getAllForms().subscribe(forms => {
expect(forms.length).toBe(4);
expect(forms[0].form).toEqual("Form 0");
expect(forms[1].form).toEqual("Form 1");
expect(forms[2].form).toEqual("Form 2");
expect(forms[3].form).toEqual("Form 3");
});
}
));
});
});

Expected one matching request for criteria

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....
});
});

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...

mock testing http - angular2

I have read many questions here and a few tutorials and I still can't get this working, I've copied the tutorials word-for-word and still no luck so I have no idea what the issue is. I'm pretty new to this stuff. Here's my code:
import { TestBed, inject, async } from '#angular/core/testing';
import { MockBackend } from '#angular/http/testing';
import {
BaseRequestOptions,
HttpModule,
Http,
Response,
ResponseOptions
} from '#angular/http';
import { ItemsService } from './items.service';
import { MOCKITEMS } from './mock-items';
describe('ItemsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [ItemsService]
});
});
it('should construct', inject([ItemsService], (service) => {
expect(service).toBeDefined();
}));
});
describe('ItemsService Mock', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ItemsService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend, opts) => new Http(backend, opts),
deps: [MockBackend, BaseRequestOptions]
}
],
imports: [HttpModule]
});
});
it('should construct', inject([ItemsService, MockBackend], (service, mockBackend) => {
expect(service).toBeDefined();
}));
describe('#getItems()', () => {
it('should return <Array<Items>>', inject([ItemsService, MockBackend], (service, mockBackend) => {
const mockReponse = {
data: [
{ itemCharId: 1, itemName: 'milk' },
{ itemCharId: 2, itemName: 'eggs' },
{ itemCharId: 3, itemName: 'meat' }
]
}
mockBackend.connections.subscribe((connection) => {
connection.mockRespond(new Response(new ResponseOptions({ body: JSON.stringify(mockReponse) })));
});
service.getItems().subscribe((items) => {
expect(items.length).toBe(3);
expect(items[0].itemName).toEqual('milk');
expect(items[1].itemName).toEqual('eggs');
expect(items[2].itemName).toEqual('meat');
});
}));
});
});
The tests are failing with expected undefined to be 3, etc. So I'm assuming it's not actually getting my mockResonse obj as the response or something on those lines? Could just be something small too I guess.
Service code:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
#Injectable()
export class ItemsService {
constructor(private http: Http) { }
getItems() {
return this.http.get('/api/items');
}
}
Any help greatly appreciated.
In the tutorial that was likely was followed, service method returns response.json().data:
getHeroes(): Promise<String[]> {
return this.http.get('myservices.de/api/heroes')
.toPromise()
.then(response => response.json().data)
.catch(e => this.handleError(e));
}
So the response is mocked as { data: [...] }.
While in the example in the question it returns this.http.get('/api/items') and doesn't call response.json().
These are the reason why there are no errors except failed assertions; items has to be equal to mockReponse.
It should be
getItems() {
return this.http.get('/api/items').map(res => res.json());
}
and
const mockReponse = [
{ itemCharId: 1, itemName: 'milk' },
{ itemCharId: 2, itemName: 'eggs' },
{ itemCharId: 3, itemName: 'meat' }
]
This can additionally be asserted with
expect(items).toEqual(mockResponse);

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