How to testing a service in Angular 7 - javascript

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

Related

Jest custom axios interceptor

i am trying to use jest with nextJS to test API. I am using a custom interceptor for all http request to have authorization token on header. Here is my interceptor code
Api.ts
import axios from 'axios';
import config from '../config/index';
const Api = () => {
const defaultOptions = {
baseURL: config.APIENDPOINT,
method: 'get',
headers: {
'Content-Type': 'application/json',
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the AUTH token for any request
instance.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
//#ts-ignore
config.headers.Authorization = token ? `${token}` : '';
return config;
});
instance.interceptors.response.use((res) => {
return res
});
return instance;
};
export default Api();
Here is the code to call the API
export const loadMerchants = async (id: any) => {
const data = await Api.get(config.APIENDPOINT + "/merchants/company/" + id)
console.log("data" ,data);
return (data)
}
And here is my test code
const axios = require('axios');
jest.mock('axios', () => {
return {
get: jest.fn(),
create: jest.fn(() => ({
interceptors: {
request: { use: jest.fn(() => Promise.resolve({ data: { foo: 'bar' } })) },
response: { use: jest.fn(() => Promise.resolve({ data: { foo: 'bar' } })) },
}
}))
}
})
it('Merchant API call', async () => {
axios.get.mockResolvedValue({
data: [
{
userId: 1,
id: 1,
title: 'My First Album'
},
{
userId: 1,
id: 2,
title: 'Album: The Sequel'
}
]
});
const merchants = await loadMerchants("1")
console.log(merchants) //always undefined
// expect(merchants).toEqual('some data');
});
on my API call if use axios.get instead of Api.get i get the correct results. I have looked into google and haven`t found any solutions.
Any help would be appreciated. Thank you.

How to test an async function with callback in Jest?

I'm having a hard time finding info on how to test this function:
const MyService = {
async stringify (entry, cb) {
try {
const response = await axios.post('localhost:3005/stringify', {
entry
})
cb(null, response.data)
} catch (minificationError) {
if (minificationError.response.status === 500) {
cb('error 1', null)
} else {
cb('error 2', null)
}
}
}
}
I understand I can import axios and mock the .post like this:
axios.post.mockResolvedValue({
data: { some: 'value' }
})
That'd work great if I the MyService was returning the promise... but how do I deal with the callback? Is this a bad practice and should the service be returning the promise and then handle errors in the component functions instead?
Additionally, how would I mock a status code with jest (to test the failed states?)
First, you have to set up mock axios after that you have to call your mockapi's in your test case
const axios = {
post: jest.fn(() => {
return Promise.resolve({
data: {},
});
}),
create: () => axios,
request: {},
defaults: {
adapter: {},
headers: {},
},
interceptors: {
request: {
use() {},
},
response: {
use() {},
},
},
};
Once you setup mock axios then you can access in your test case and return whatever mock response and status code you want.
mockAxios.post.mockImplementation((url) => {
if (url.includes("something")) {
return Promise.resolve({ data:{"response":""}, status: 200 });
}
return Promise.reject(new Error("not found"));
});

unit-test with HTTP request returns Promise { <pending> }

I am using axios mock adapter to mock HTTP request to test my function. After I defined the behaviour for the function, and then I created an instance of the class to call the function, the result is
**Promise { <pending> }**,
what is the problem? how can I return the value I defined?
Here is my code:
UserService.js
export default class UserService {
getUserInfo = userId => {
const params = {
userId,
};
return axios
.get('https://www.usefortesting.com', {
params: { userId: params },
})
.then(response => response.data.userInfo)
.catch(error => error);
};
}
UserService.test.js
import React from 'react';
import axios from 'axios';
import UserService from './UserService';
import MockAdapter from 'axios-mock-adapter';
describe('testing', () => {
let axiosMock;
const Info = {
userInfo: {
id: '123',
name: 'omg',
},
};
beforeEach(function() {
axiosMock = new MockAdapter(axios);
});
afterEach(() => {
axiosMock.reset();
axiosMock.restore();
});
it('testing', () => {
axiosMock
.onGet('https://www.usefortesting.com', {
params: { userId: 'user_1' },
})
.reply(200, Info);
let userService = new UserService();
let response = userService.getUserInfo('user_1');
console.log(response);
});
});
You need to await for response in your test. Either use callbacks or async/await as shown below.
Your test should be like this:
it('testing', async () => { // notice async here
axiosMock
.onGet('https://www.usefortesting.com', {
params: { userId: 'user_1' },
})
.reply(200, Info);
let userService = new UserService();
let response = await userService.getUserInfo('user_1'); // notice await here
console.log(response);
});
OR
it('testing', () => {
...
userService.getUserInfo('user_1').then(response => {
console.log(response);
});
});
You can check this link on jest docs for more examples.
Also there is error in your getUserInfo() method, in params you are passing an object for userId but you need to pass string or int. What you should do is:
return axios.get('https://www.usefortesting.com', {
params: { userId: params.userId },
})...
OR
return axios.get('https://www.usefortesting.com', {
params,
})...

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

Categories

Resources