Can't access subscribe method in unit-test - json

I'm experiencing a very strange issue on a simple unit-test of a simple service get method. I'm using Angular 4 and cli so karma/jasmine for testing.
My service method:
getAccounts(): Observable<ApiData> {
return this.authHttp.get('../../assets/mock_data/productsOverview.json')
.map(
data => {
return JSON.parse(data['_body']);
},
err => {
return err;
}
);
}
And here is my test case:
import { TestBed, inject, async } from '#angular/core/testing';
import { Http, BaseRequestOptions, RequestOptions, HttpModule, ConnectionBackend } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { AuthHttp, AuthConfig } from 'angular2-jwt';
import { encodeTestToken } from 'angular2-jwt/angular2-jwt-test-helpers';
import { AppModule } from '../app.module';
import { PersonalAccountsService, ApiData } from './personal-accounts.service';
import { Observable } from 'rxjs/Observable';
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({
headerName: 'Authorization',
headerPrefix: 'Bearer',
tokenName: 'token',
tokenGetter: (() => localStorage.getItem('token')),
globalHeaders: [
{ 'Content-Type': 'application/json' },
{ 'Ocp-Apim-Subscription-Key': localStorage.getItem('key') }],
}), http, options);
}
const MyMockedService = {
method: () => { }
}
describe('PersonalAccountsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PersonalAccountsService,
BaseRequestOptions,
MockBackend,
{
provide: Http,
useFactory: (mockBackend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(mockBackend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions],
},
{
provide: AuthHttp,
useFactory: (http) => {
return new AuthHttp(new AuthConfig({
tokenName: 'token',
tokenGetter: (() => encodeTestToken(this)),
globalHeaders: [{ 'Content-Type': 'application/json' }]
}), http);
},
deps: [Http],
},
],
imports: [
HttpModule
],
});
});
it('should be created', inject([PersonalAccountsService],
(service: PersonalAccountsService) => {
expect(service).toBeTruthy();
}));
it('identification', async(
inject(
[PersonalAccountsService],
(service: PersonalAccountsService) => {
console.log('ENTERS HERE');
service.getAccounts().subscribe(
data => {
console.log('NOT ENTERING HERE');
},
err => {
console.log('NOT ENTERING HERE');
},
() => {
console.log('NOT ENTERING HERE')
}
);
}
)
));
});
Why this is happening? I'm trying to figure it our for a long time and I cannot even take an error to dive deeper.

The test likely performs requests but they weren't mocked with MockBackend.
The fact that code relies on third-party services (AuthHttp) makes it integration test rather than unit test.
To keep it as isolated as possible, every unit but tested unit should be mocked or stubbed. The service can be tested in isolation (in this case DI is not tested) or with TestBed:
beforeEach(() => {
const authHttpMock = jasmine.createSpyObj('authHttp', ['get']);
TestBed.configureTestingModule({
providers: [
{ provide: AuthHttp, useValue: authHttpMock }
...
});
it('...', async(inject([PersonalAccountsService, AuthHttp], async (service, authHttpMock) => {
const responseMock = Observable.of({ _body: ... });
authHttpMock.get.and.returnValue(responseMock);
const accounts$ = service.getAccounts();
expect(authHttpMock.get).toHaveBeenCalledWith(...);
const accounts = await accounts$.toPromise();
expect(accounts)...
...

Related

Fetch post API returning empty array in react native iphone app but working well in postman

I have a fetch API to get values from mysql database.Below is my screen code in which I need to get data from API:
TargetSetUpPage.js:
import React, {
useState,
useEffect,
useReducer,
useSelector,
Component,
} from "react";
import { StyleSheet, Text, Button, TextInput, ScrollView } from "react-native";
import * as authActions from "../../store/actions/auth";
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const onScreenLoad = () => {
let action;
action = authActions.getDeviceInfo();
};
useEffect(() => {
onScreenLoad();
});
return (
<ScrollView showsVerticalScrollIndicator={true}>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button
title="Add"
// onPress = {() => }
/>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},
buttonleft: {
paddingTop: 40,
height: 40,
width: "80%",
},
});
export default TargetSetUpPage;
Below is the store code which calls fetch API.
auth.js
import AsyncStorage from "#react-native-community/async-storage";
import Device from "../../model/Device";
export const LOGIN = "LOGIN";
export const LOGOUT = "LOGOUT";
export const GETDEVICEINFO = "GETDEVICEINFO";
export const login = (textemailid, textpassword) => {
const formData = new FormData();
formData.append("txtUemail", textemailid);
formData.append("txtUpass", textpassword);
return async (dispatch) => {
fetch("https://------------------------/login.php", {
method: "post",
body: formData,
})
.then((res) => res.text())
.then((loginresult) => {})
.catch((err) => {
console.log(err);
});
const saveDataToStorage = (loginresult) => {
AsyncStorage.setItem(
"userData",
JSON.stringify({
loginresult: loginresult,
})
);
};
dispatch({ type: LOGIN });
};
};
export const logout = () => {
return { type: LOGOUT };
};
export const getUserInfo = (textemailid) => {
const formData = new FormData();
formData.append("txtEmail", textemailid);
return async (dispatch) => {
fetch("https://------------------------/getUserInformation.php", {
method: "post",
body: formData,
})
.then((res) => res.json())
.then((getuseridresult) => {
const userid = getuseridresult.map((d) => d.id);
saveDataToStorage(userid);
})
.catch((err) => {
console.log(err);
});
const saveDataToStorage = async (userid) => {
try {
await AsyncStorage.setItem(
"userDatauserid",
JSON.stringify({
userid: userid,
})
);
} catch (e) {
alert("not saved");
}
};
};
};
export const getDeviceInfo = async () => {
const useridfordevices = await AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const { userid } = obj;
var userid1 = userid[0];
console.log("txtUserId is " + userid1);
const formData = new FormData();
formData.append("txtUserId", userid1);
console.log(formData);
return async (dispatch) => {
fetch("https://-------------------------------/getDeviceInformation.php", {
method: "post",
body: formData,
})
.then((res) => res.json())
.then((result) => {
console.log("Hi" + result);
})
.catch((err) => {
console.log(err);
});
};
};
getDeviceInfo function in the above auth.js is not returning anything. I am sending correct data to fetch API as below:
txtUserId is 616718042ad26
FormData {
"_parts": Array [
Array [
"txtUserId",
"616718042ad26",
],
],
In postman I am getting the below JSON data:
[
{
"targetid": "TargetDevice1",
"targetname": "device_1",
"userid": "616718042ad26"
},
{
"targetid": "TargetDevice2",
"targetname": "device_2",
"userid": "616718042ad26"
}
]
Import and add dispatch in TargetSetUpPage:
TargetSetUpPage.js:
import React, {
useState,
useEffect,
useReducer,
useSelector,
Component,
} from "react";
import { StyleSheet, Text, Button, TextInput, ScrollView } from "react-
native";
import { useDispatch } from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-community/async-storage';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const onScreenLoad = async() => {
const useridfordevices = await AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const { userid } = obj;
var userid1 = userid[0];
console.log("txtUserId is " + userid1);
let action;
action = authActions.getDeviceInfo(
userid1
);
await dispatch(action);
};
useEffect(() => {
onScreenLoad();
});
return (
<ScrollView showsVerticalScrollIndicator={true}>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button
title="Add"
// onPress = {() => }
/>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},
buttonleft: {
paddingTop: 40,
height: 40,
width: "80%",
},
});
export default TargetSetUpPage;
In the store, dispatch the action to TargetSetUpPage.js from getDeviceInfo function.
auth.js:
export const getDeviceInfo = (userid1) => {
const formData = new FormData();
formData.append("txtUserId", userid1);
return async dispatch => {
fetch('https://----------/getDeviceInformation.php',
{
method:'post',
body: formData
})
.then((res) => res.json())
.then((getuserdeviceinfo) => {
const loadDevices = [];
loadDevices.push(
new Device(
getuserdeviceinfo.map((d) => d.targetid),
getuserdeviceinfo.map((d) => d.targetname),
)
);
console.log(loadDevices);
})
.catch((err) =>{
console.log(err);
})
dispatch({type:GETDEVICEINFO,availableDevice:loadDevices})
}
}
This displays the Device array from mysql database.
Array [
Device {
"TargetId": Array [
"jtgTargetDevice1",
"jtgTargetDevice2",
],
"TargetName": Array [
"device_1",
"device_2",
],
},
]

Unit Testing Angular component with service : Cannot read property 'diagonisticData' of undefi

I am new to angular testing. I have a component, nested json and a service. The app works fine but during testing values are not being populated into the component. Please help.I have attached the service, json object,component and spec file.
I am not sure if I am following the right approach in spec file.
App component -Hub-Details-component.ts
export class HubDetailsComponent implements OnInit {
ngOnInit(): void {}
public jsonData:any = []
public diagnosticsData:any = [];
public dummy:any = [];
public hubData:any;
constructor(private dataService: DataService) {}
handleData()
{
this.dataService.getData()
.subscribe(response =>{
if(response!=null)
{
this.jsonData=response;
console.log(this.jsonData);
this.dummy=this.jsonData.result;
console.log(this.dummy);
this.diagnosticsData=this.dummy.diagnosticData;
const DataArray = [];
for(const element in this.diagnosticsData)
{
DataArray.push({
id:element,
name:this.diagnosticsData[element]
});
}
console.log(DataArray);
this.hubData=DataArray;
}
});
}
}
DataService
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'})
export class DataService {
public url = '/assets/Data/Data.json'
constructor(private http: HttpClient ) { }
getData = () => {
const url = 'assets/Data/Data.json';
return this.http.get(url);
}}
json file
{
"result"
{
"abc"
{
"name" :"abc",
"tag" : "xyz",
"status": "qwe"
}
}
}
spec.ts
it('should get data from dataservice',fakeAsync(()=>{
const fixture =
TestBed.createComponent(HubDetailsComponent);
const component =
fixture.debugElement.componentInstance;
const service =
fixture.debugElement.injector.get(DataService);
let spy_getPosts =
spyOn(service,'getData').and.callFake(() => {
return of([{"result"{
"abc"
{
"name" :"abc",
"tag" : "xyz",
"status": "qwe"
}
}
}]).pipe(delay(2000));});
fixture.detectChanges();
component.handleData();
tick(2000);
expect(component.jsonData).toEqual([{
{"result"{
"abc"
{
"name" :"abc",
"tag" : "xyz",
"status": "qwe"
}
}
}
}]);
}));
Thanks in advance.
Try this:
// In your spec file, mock the service;
#Injectable()
class MockDataService extends DataService {
getData() {
const mockData = {
result: {
diagnosticData: [
{ mock1: 'value1' },
{ mock2: 'value2' }
]
}
}
return of(mockData);
}
}
describe('Your Component Name you are testing', () => {
let dataService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [],
imports: [...yourImports],
schemas: [NO_ERRORS_SCHEMA],
providers: [
{
provide: DataService,
useClass: MockDataService
}
]
}).compileComponents();
dataService = TestBed.get(DataService);
}));
// Now your test case:
it('should call #handleData() method', () => {
spyOn(component, 'handleData').and.callThrough();
component.handleData();
expect(component.handleData).toHaveBeenCalled();
});
// Thats it. You do not need to do anything else;
})

How to fix JSON parse error in Vuex with localStorage

I have setup Vuex in Vue.js and using it to update the state. After building login functionality on it I am trying to store the token in localstorage but when I add localstorage to state it is throwing an error.
my current code:
import Vue from 'vue';
import Vuex from 'vuex';
import { getAPI } from '#/axios';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
accessToken: JSON.parse(localStorage.getItem('accessToken')) || null,
APIData: '',
},
mutations: {
// eslint-disable-next-line camelcase
updateStorage(state, { access_token }) {
// eslint-disable-next-line camelcase
state.accessToken = access_token;
localStorage.setItem('accessToken', JSON.stringify(access_token));
// axios.defaults.headers.common.Authorization = `Bearer ${access_token.access_token}`;
// sessionStorage.setItem('accessToken', access_token);
},
destroyToken(state) {
state.accessToken = null;
},
},
getters: {
loggedIn(state) {
return state.accessToken != null;
},
},
actions: {
userLogin(context, usercredentials) {
return new Promise((resolve, reject) => {
getAPI.post('/login', {
email: usercredentials.email,
password: usercredentials.password,
})
.then((response) => {
context.commit('updateStorage', { access_token: response.data.access_token });
resolve();
console.log(response.data.access_token);
})
.catch((error) => {
reject(error);
});
});
},
userLogout(context) {
if (context.getters.loggedIn) {
context.commit('destroyToken');
}
},
},
});
Since you're receiving raw encoding and creating an object wrapper for it in this format:
{ access_token: 'eyJ0eX...' }
You shouldn't destructure it in the mutation payload. Pass the whole object to localStorage if you're going to use JSON.parse:
updateStorage(state, access_token) {
state.accessToken = access_token;
localStorage.setItem('accessToken', JSON.stringify(access_token));
},

How can i put the data array gained from the fetch return to the Dropdown component?

I'm trying to make a simple dropdown list which data is gained from a fetch return..
if I use console to view the return, it shows like this :
[
{
"ID": "BOGOR~10"
"Location": "BOGOR"
},
{
"ID": "JADETABEK~16"
"Location": "JADETABEK"
}
]
if I want to take the location BOGOR and JADETABEK and put them into a Dropdown, how can I do that? this is my testing class
import React , { Component } from 'react';
import { View , StyleSheet , Text , Dimensions } from 'react-native';
import { Dropdown } from 'react-native-material-dropdown';
const ScreenWidth = Dimensions.get('window').width;
const Screenheight = Dimensions.get('window').height;
export default class testing extends Component {
constructor(props) {
super(props)
this.state = {
data: []
}
}
componentDidMount() {
fetch(url , {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({"lokasi":
{
}
})
})
.then(response => response.json())
.then(res => {
this.setState({
data: res.PilihLokasiResult.Lokasi
})
alert(res.PilihLokasiResult.Lokasi)
})
}
render() {
return(
<View style={styles.container}>
<View>
<Text>{this.state.location}</Text>
<Dropdown label="select location" style={{width: 400 }}/>
</View>
</View>
)
}
}
You need to format the data since react-native-material-dropdown accepts data in the form of {value: 'Sample'}
this.state = {
data: [],
dropDownData: []
}
const formatData = (data) => {
return data.map(dataObj => {
return {value: dataObj.Location} // return based on location
})
}
.then(res => {
const dropDownData = formatData(res.PilihLokasiResult.Lokasi)
this.setState({
data: res.PilihLokasiResult.Lokasi,
dropDownData
})
})
<Dropdown label="select location" data={this.state.dropDownData} style={{width: 400 }}/>

Angular2 Ansychronous bootstrapping with external json configuration file

I have upgraded to angular2 RC6 and want to load an external JSON config file before bootstrapping my AppModule. I had this working before RC5 but am now having trouble finding an an equivalent way of injecting this data.
/** Create dummy XSRF Strategy for Http. */
const XRSF_MOCK = provide(XSRFStrategy, { provide: XSRFStrategy, useValue: new FakeXSRFStrategyService() });
/** Create new DI. */
var injector = ReflectiveInjector.resolveAndCreate([ConfigService, HTTP_PROVIDERS, XRSF_MOCK]);
/** Get Http via DI. */
var http = injector.get(Http);
/** Http load config file before bootstrapping app. */
http.get('./config.json').map(res => res.json())
.subscribe(data => {
/** Load JSON response into ConfigService. */
let jsonConfig: ConfigService = new ConfigService();
jsonConfig.fromJson(data);
/** Bootstrap AppCOmponent. */
bootstrap(AppComponent, [..., provide(ConfigService, { useValue: jsonConfig })
])
.catch(err => console.error(err));
});
This worked just fine but struggling to change to work with RC6.
I'm experimenting the following approach but struggling to modify my predefined AppModule with loaded data:
const platform = platformBrowserDynamic();
if (XMLHttpRequest) { // Mozilla, Safari, ...
request = new XMLHttpRequest();
} else if (ActiveXObject) { // IE
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
console.log(e);
}
}
}
request.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
var json = JSON.parse(this.responseText);
let jsonConfig: ConfigService = new ConfigService();
jsonConfig.fromJson(json);
/**** How do I pass jsConfig object into my AppModule here?? ****/
platform.bootstrapModule(AppModule);
}
};
// Open, send.
request.open('GET', './config.json', true);
request.send(null);
I had the same problem. Looks like you came across my Gist :-)
As far as the RC 6 update, you should check out the HttpModule source. It shows all the providers that were originally in the now removed HTTP_PROVIDERS. I just checked that out and came up with the following
function getHttp(): Http {
let providers = [
{
provide: Http, useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new Http(backend, options);
},
deps: [XHRBackend, RequestOptions]
},
BrowserXhr,
{ provide: RequestOptions, useClass: BaseRequestOptions },
{ provide: ResponseOptions, useClass: BaseResponseOptions },
XHRBackend,
{ provide: XSRFStrategy, useValue: new NoopCookieXSRFStrategy() },
];
return ReflectiveInjector.resolveAndCreate(providers).get(Http);
}
As far as the
/**** How do I pass jsConfig object into my AppModule here?? ****/
platform.bootstrapModule(AppModule);
It's not the prettiest (it's really not that bad), but I found something I didn't even know was possible, from this post. Looks like you can declare the module inside the function.
function getAppModule(conf) {
#NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
bootstrap: [ AppComponent ],
providers: [
{ provide: Configuration, useValue: conf }
]
})
class AppModule {
}
return AppModule;
}
Below is what I just used to test right now
import { ReflectiveInjector, Injectable, OpaqueToken, Injector } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
import {
Http, CookieXSRFStrategy, XSRFStrategy, RequestOptions, BaseRequestOptions,
ResponseOptions, BaseResponseOptions, XHRBackend, BrowserXhr, Response
} from '#angular/http';
import { AppComponent } from './app.component';
import { Configuration } from './configuration';
class NoopCookieXSRFStrategy extends CookieXSRFStrategy {
configureRequest(request) {
// noop
}
}
function getHttp(): Http {
let providers = [
{
provide: Http, useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new Http(backend, options);
},
deps: [XHRBackend, RequestOptions]
},
BrowserXhr,
{ provide: RequestOptions, useClass: BaseRequestOptions },
{ provide: ResponseOptions, useClass: BaseResponseOptions },
XHRBackend,
{ provide: XSRFStrategy, useValue: new NoopCookieXSRFStrategy() },
];
return ReflectiveInjector.resolveAndCreate(providers).get(Http);
}
function getAppModule(conf) {
#NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
bootstrap: [ AppComponent ],
providers: [
{ provide: Configuration, useValue: conf }
]
})
class AppModule {
}
return AppModule;
}
getHttp().get('/app/config.json').toPromise()
.then((res: Response) => {
let conf = res.json();
platformBrowserDynamic().bootstrapModule(getAppModule(conf));
})
.catch(error => { console.error(error) });