I have created a service which sends a request to the backend and makes the result available to the component via an observable.
export class QuestionManagementService {
questionsArray$: Observable<Result>;
private questionsArraySubject: BehaviorSubject<Result>; //to send list of questions
...
constructor(private http: HttpClient, private helper:HelperService, private bs:WebToBackendInterfaceService, private loaderService:LoaderService) {
this.questionsArraySubject = new BehaviorSubject<Result>(new Result('initial',{})); //A Subject can act both as an Observable and an Observer
this.questionsArray$ = this.questionsArraySubject.asObservable(); //create Observable. Other components can subcribe to it now to get notifications/values
...
}
//this method sends the request to network via another `bs` service. The request is sent using `http.post`
getQuestions(questionFilter:GetQuestionsfilter){
console.log("In QuestionManagementService: getQuestions");
let observable:Observable<HttpEvent<any>> = this.bs.getQuestions(questionFilter);
let subscription:Subscription = observable.subscribe((ev:HttpEvent<any>)=>{
if(ev.type === HttpEventType.Response) { //null means that the response wasn't an HttpResponse but probably some internal Rxjs event (eg type 0)
let response= <HttpResponse<any>>ev;
console.log("http response received: ",response);
//should remove the token from storage
console.log('response body from server: ',ev.body);
let isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(ev.body);
if (isResponseStructureOK) {
console.log("response structure is OK");
let response: ServerResponseAPI = ev.body;
let result:string = response.result;
console.log("result is : " + result);
/*if result could be success or error*/
/*additionalInformation is a string and the string contains a valid json which has array of questions
in format {"questions-list":[{"tag":"some tag1","description":"some description1"},{{"tag":"some tag2","description":"some description2"},...]}
*/
let message:string = response['additional-info'];
console.log("message is "+message);
if(result === "success") {
let jsonQuestionList: string = response['additional-info'];
console.log("jsonQuestionList response as string: ", jsonQuestionList);
//let jsonQuestions: PracticeQuestionsListAPI = JSON.parse(jsonQuestionList);
//console.log("jsonQuestion array:", jsonQuestions);
//this.questionsArraySubject.next(jsonQuestions['questions-list']);
this.questionsArraySubject.next(new Result('success', response["additional-info"]));
} else {
this.questionsArraySubject.next(new Result('error', response["additional-info"]));
}
}
else {
/**
* If something goes wrong, send error rather than send next with result="error"
*/
console.log("received incorrect response structure from server: ", ev.body);
//TODOM - need to change hard coded responses and pick them from a config or global variable.
this.questionsArraySubject.error(new Result('error',"Invalid response structure from server"));
}
}
else {
console.log("not response. ignoring");
}
},
(error:ServerResponseAPI)=>{/*web to backend service will send error in ServerResponseAPI format. This is what handleError throws*/
console.log("got error from the Observable: ",error);
this.questionsArraySubject.error(new Result('error',error['additional-info']));
},
()=>{ //observable complete
console.log("observable completed")
});
}
}
The following component subscribes to this service.
export class PraticeQuestionListComponent implements OnInit, OnDestroy {
questions: PracticeQuestionsListAPI; //the result from observable will be stored here.
questionListSubscription:Subscription; //reference of the subscription
ngOnDestroy(): void {
console.log("destroying component. unsubscribing");
this.questionListSubscription.unsubscribe()
}
//on initialisation, I subscribe to the observable
ngOnInit(){
console.log("in question list on init. question is ",this.questions);
...
this.questions= new PracticeQuestionsListAPI(new AdditionalPagingInfo("",new PartitionInfo(0,0)),
[]);
let tagSubscription = this.questionManagementService.getSupportedTags(new TagId("coding"));
console.log("subscribing to question mgmt service");
this.questionListSubscription = this.questionManagementService.questionsArray$.subscribe((result:Result)=>{
console.log('received result from question mgmgt service - array observable',result);
if(result.result === "success") { //received response from server
let questionList = JSON.parse(result.additionalInfo) as PracticeQuestionsListAPI;
console.log("got list of questions value ", questionList);
this.questions['pagination-info'] = questionList['pagination-info'];
this.questions['questions-list'] = questionList['questions-list'];
/*
0 length of questions-list means no questions.
this could be response from the server indicating that there are no more questions
*/
/*
* the server indicates that there are no more questions by either sending empty question list or by sending
* 0 values for pagination state and partition info
*/
if (questionList["questions-list"].length !== 0) { //server has send list of questions
this.questions['pagination-info']['page-state'] = questionList['pagination-info']['page-state'];
this.questions['pagination-info']['partition-info'] = questionList['pagination-info']['partition-info'];
this.questions['questions-list'] = questionList['questions-list'];
console.log("previous question filter is ",this.questionsFilter);
this.questionsFilter["pagination-info"]["page-state"]=questionList["pagination-info"]["page-state"];
this.questionsFilter["pagination-info"]["partition-info"].month=questionList["pagination-info"]["partition-info"].month;
this.questionsFilter["pagination-info"]["partition-info"].year=questionList["pagination-info"]["partition-info"].year;
console.log("new question filter is ",this.questionsFilter);
//TODOM - maybe this assignment below was causing memory leak. So changed this as above
//this.questionsFilter['pagination-info'] = questionList['pagination-info'];
this.lastPage = false; //the server indicates that there are no more questions by sending these values (no paging state and no partition info)
if (this.questions['pagination-info']['page-state'].length == 0 &&
this.questions['pagination-info']['partition-info'].year == 0 &&
this.questions['pagination-info']['partition-info'].month == 0) {
this.lastPage = true;
} else {//if the list is empty then there are no (more) questions for the selected tag
this.lastPage = false;
}
} else {
this.lastPage = true; //Don't show next button if there are no questions.
this.showDialog(new PracticeQuestionListContext("Reached end of the search. No more results available", new PracticeQuestionListAdditionalInfo()));
}
} else {
//TODOM - I should probably display the error in case there is an error from the server
console.log("ignoring value");
}
},
(err:Result)=>{
console.log("received error from QuestionArray observable",err);
//TODOM - probably should change the name of DialogContext to Component specific additional context
this.showDialog(new PracticeQuestionListContext(err.additionalInfo,new PracticeQuestionListAdditionalInfo()));
},
()=>{
console.log("question mgmt service, questionarray observable completed.");
});
}
}
The issue I am facing is that if I visit the component for the first time, I get the values from the observable after making some selections in the UI (expected behavior). Then i visit the home page of the application, the component gets destroyed (again, expected behavior). Then if I visit the component again, the observable sends the old values (from the first visit) even when I have not made any UI selections.
Why is the observable sending old values and how can I stop it from doing that? I have created another question in SO with pictures which might explain the scenario better
angular component is retaining old value maybe because the observable is resending past data
Related
I'm trying to use Firebase and its callable Cloud Functions for my Unity project.
With the docs and different posts I found on the web I struggle to understand how returning data works. (I come from Azure Functions in C#)
I use TypeScript, and try to return a custom object CharactersResponse:
export class CharactersResponse //extends CustomResponse
{
Code!: CharactersCode;
CharacterID?: string;
}
export enum CharactersCode
{
Success = 0,
InvalidName = 2000,
CharacterNameAlreadyExists = 2009,
NoCharacterSlotAvailable = 3000,
InvalidCharacterClass = 4000,
EmptyResponse = 9000,
UnknownError = 9999,
}
(Custom Response is a parent class with only an UnknownErrorMessage string property, that I use for adding extra message when needed, but only in Unity. I don't need it in my functions.)
I have the same in my C# Unity Project:
public class CharactersResponse : CustomResponse
{
public CharactersCode Code;
public string CharacterID;
}
public enum CharactersCode
{
Success = 0,
InvalidName = 2000,
CharacterNameAlreadyExists = 2009,
NoCharacterSlotAvailable = 3000,
InvalidCharacterClass = 4000,
EmptyResponse = 9000,
UnknownError = 9999,
}
I'm still learning but I found it useful to do this way for displaying correct messages in Unity (and also regarding localization).
When the Code is 0 (Success), I will usually need to get some data at the same time like in this example CharacterID, or CharacterLevel, CharacterName etc.. CharacterResponse will be used for all functions regarding Characters like "GetAllCharacters", "CreateNewCharacter" etc..
My Function (CreateNewCharacter) looks like this:
import * as functions from "firebase-functions";
import { initializeApp } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { CharactersResponse } from "./CharactersResponse";
import { CharactersCode } from "./CharactersResponse";
import { StringUtils } from "../Utils/StringUtils";
// DATABASE INITIALIZATION
initializeApp();
const db = getFirestore();
// CREATE NEW CHARACTER
export const CreateNewCharacter =
functions.https.onCall((data, context) =>
{
// Checking that the user is authenticated.
if (!context.auth)
{
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
// TEST
data.text = '';
// Authentication / user information is automatically added to the request.
const uid: string = context?.auth?.uid;
const characterName: string = data.text;
// Check if UserID is present
if (StringUtils.isNullOrEmpty(uid))
{
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'Missing UserID in Auth Context.');
}
const response = new CharactersResponse();
if (StringUtils.isNullOrEmpty(characterName))
{
response.Code = CharactersCode.InvalidName;
console.log("character name null or empty return");
return response; // PROBLEM IS HERE *****************
}
console.log("end return");
return "Character created is named : " + characterName + ". UID = " + uid;
});
In Unity, the function call looks like this:
private static FirebaseFunctions functions = FirebaseManager.Instance.Func;
public static void CreateNewCharacter(string text, Action<CharactersResponse> successCallback, Action<CharactersResponse> failureCallback)
{
Debug.Log("Preparing Function");
// Create the arguments to the callable function.
var data = new Dictionary<string, object>();
data["text"] = text;
// Call the function and extract the operation from the result.
HttpsCallableReference function = functions.GetHttpsCallable("CreateNewCharacter");
function.CallAsync(data).ContinueWithOnMainThread((task) =>
{
if (task.IsFaulted)
{
foreach(var inner in task.Exception.InnerExceptions)
{
if (inner is FunctionsException)
{
var e = (FunctionsException)inner;
// Function error code, will be INTERNAL if the failure
// was not handled properly in the function call.
var code = e.ErrorCode;
var message = e.Message;
Debug.LogError($"Code: {code} // Message: {message}");
if (failureCallback != null)
{
failureCallback.Invoke(new CharactersResponse()
{
Code = CharacterCode.UnknownError,
UnknownErrorMessage = $"ERROR: {code} : {message?.ToString()}"
});
}
}
}
}
else
{
Debug.Log("About to Deserialize response");
// PROBLEM IS HERE *********************
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
Debug.Log("Deserialized response");
if (response == null)
{
Debug.LogError("Response is NULL");
}
else
{
Debug.Log("ELSE");
Debug.Log($"Response: {response}");
Debug.Log(response.Code.ToString());
}
}
});
}
The problem :
In my Unity C# code, task.Result.Data contains the CharactersCode I've set in my function, but I can't find a way to convert it to CharactersResponse. (It worked in Azure Functions). Moreover, the line just after Deserialization Debug.Log("Deserialized response"); is not executed. The code seems stuck in the deserialization process.
I tried with and without extending my TypeScript class with CustomResponse(because I don't need it in my Function so I didn't extended it at first).
I also tried setting a CharacterID because I thought maybe it didn't like the fact that this property was missing but the result is the same.
I don't understand what is the problem here? If any of you can help.
Thanks.
HttpsCallableResult.Data is of type object!
=> Your ToString will simply return the type name something like
System.Object
or in your case the result is a dictionary so it prints out that type.
=> This is of course no valid JSON content and not what you expected.
Simply construct the result yourself from the data:
var result = (Dictionary<string, object>)task.Result.Data;
CharactersResponse response = new CharactersResponse
{
Code = (CharactersCode)(int)result["Code"],
CharacterID = (string)result["CharacterID"];
};
I wanted to implement derHugo's solution but couldn't find a way to convert task.Result.Data to Dictionary<string, object>.
The code was stuck at var result = (Dictionary<string, object>)task.Result.Data; even in step by step debugging and no error popped up.
OLD SOLUTION:
So I did a little research and stumbled upon this post and ended up using this instead :
var json = JsonConvert.SerializeObject(task.Result.Data);
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(json);
I basically convert the task.Result.Data to JSON and convert it back to CharactersResponse and it works. I have what I wanted.
However, I seem to understand that it is not the best solution performance-wise, but for now it is okay and I can now move forward in the project, I'll try to find a better solution later.
NEW SOLUTION:
I wanted to try one last thing, out of curiosity. I wondered what if I convert to JSON at the beginning (in my function) instead of at the end (in my Unity app). So I did this in my function's TypeScript code:
response.Code = CharactersCode.InvalidName;
var r = JSON.stringify(response); // Added this line
return r; // return 'r' instead of 'response'
In my C# code, I retried this line of code:
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
And it works ! I just needed to convert my object to JSON in my function before returning it. It allows me to "save" one line of code to process on the client side compared to the old solution.
Thanks derHugo for your answer as it helped me finding what I want.
I've been trying to make a very basic app pulling user information from a .json file and "logging them in", and have a json storing whether a user is logged into the app and their user ID. I'm stuck on a method which would take the given email and password, match them to an entry in the users json, then update the login json with the new information (login true, and user ID.) This is what I have so far in the method:
setUserLogIn(email, password):any{
if (this.users){
this.users.forEach(foundUser => {
if (foundUser.email === email && foundUser.password === password){
this.currentUser=foundUser;
let login:Login = {"id": 1, "loginStatus":true, "userId":foundUser.id}
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log) //this isn't reached, never prints in console
if (log !== undefined){
return true;
}
return false;
}))
}
if (this.currentUser != null){
FetchUserService.isLoggedIn = true;
} else{
FetchUserService.isLoggedIn = false;
}
})
}
}
From my previous tests I know everything else in the method works correctly, just the put only returns undefined. I am subscribing to the method in the controller:
this.fetchUserService.setUserLogIn(this.userEmail, this.userPassword).subscribe(data => {
console.log(data);
})
This method of subscription returns an error. I also tried subscribing in the service itself, like:
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log)
if (log !== undefined){
log.subscribe(data => {
return data
})
Taking this into the component and logging the result also just returns undefined.
Any suggestions? I have no idea what I'm doing wrong, after searching put methods for the past few hours I can't see any differences in what I have there. Any help is greatly appreciated!
There are multiple issues here.
Parallel subscriptions. Avoid them if possible. Here you could use forkJoin to combine all observables and trigger them in parallel.
Why would an HTTP request emit an Observable as it's response? Most probably it wouldn't.
Currently you aren't returning anything from the function.
Try the following
setUserLogIn (email, password): Observable<any> { // <-- return `Observable` here
if (!this.users) return NEVER;
return forkJoin(
this.users.map(foundUser => {
if (foundUser.email === email && foundUser.password === password) {
this.currentUser = foundUser;
FetchUserService.isLoggedIn = true;
let login: Login = {
"id": 1,
"loginStatus": true,
"userId": foundUser.id
};
return this.httpService.put('http://localhost:7800/loginCheck/1', login).pipe(
map((log: Login) => { // why would an HTTP request emit an observable?
console.log(log);
return (!!log);
})
);
}
FetchUserService.isLoggedIn = false;
return EMPTY; // `forkJoin` emits only when all observables complete
})
);
}
I am trying to figure out how to parse the JSON data that I am getting from the Spotify API. I am using this node module https://www.npmjs.com/package/spotify-web-api-js to get Spotify playlist tracks.
I am using this to GET my json (see what I did there)
export class HomePage {
spotifyApi = new SpotifyWebApi;
constructor() {}
}
var spotifyApi = new SpotifyWebApi();
spotifyApi.setAccessToken('Spotify OAuth Token');
spotifyApi.getPlaylistTracks('37i9dQZEVXbMDoHDwVN2tF')
.then(function(data) {
console.log('Playlist Tracks', data);
}, function(err) {
console.error(err);
var prev = null;
function onUserInput(queryTerm) {
// abort previous request, if any
if (prev !== null) {
prev.abort();
}
// store the current promise in case we need to abort it
prev = spotifyApi.searchTracks(queryTerm, {limit: 5});
prev.then(function(data) {
// clean the promise so it doesn't call abort
prev = null;
// ...render list of search results...
}, function(err) {
console.error(err);
});
}
This returns a JSON file but for some reason (probably my mistake) when I use JSON.parse(data);
console.log(data.name) it doesn't work (I know I am doing something wrong here but I don't know how to fix it). Thanks in advance :{)
If you want to get the tracks from the url you have to do this data.tracks.track[0] replace 0 with the needed tracks.
My Angular application has a service QuestionManagementService which response on a BackendService to send REST messages. The BackendService in turn uses HttpClient. I am trying to test the QuestionManagementService in isolation.
The method I am testing is addQuestion
addQuestion(question:PracticeQuestion):any{
console.log("In QuestionManagementService: addQuestion: ",question);
this.bs.createQuestion(question).subscribe((res)=>{
console.log("add practice question - response is ",res);//I EXPECT THESE PRINTS TO SHOW BUT THEY DON'T
let ev = <HttpEvent<any>>(res);
if(ev.type === HttpEventType.Response) {
console.log('response from server: returning body '+ev.body);
let isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(ev.body);
if (isResponseStructureOK) {
let response:ServerResponseAPI = ev.body;
console.log("received response from server: " + response.result);
this.addQuestionSubject.next(new Result(response.result,response['additional-info']));
} else {
console.log("received incorrect response structure from server: ", ev.body);
this.addQuestionSubject.next(new Result('error','Invalid response structure from server'));
}
}
else {
console.log("not response. ignoring");
}
},
(error:ServerResponseAPI)=>{
console.log("got error from the Observable: ",error);
let errorMessage:string = this.helper.userFriendlyErrorMessage(error);
this.addQuestionSubject.next(new Result('error',errorMessage));//TODOM - need to standardise errors
},
()=>{ //observable complete
console.log("observable completed")
});
}
As I am doing unit testing of addQuestion, I thought that I can mock the createQuestion method of the BackendService. The spec I have written so far is the following but I don't think it is correct as I don't see any prints on the console when the mocked response of createQuestion is received.
fit('should add a question',()=>{
let backendService = TestBed.get(WebToBackendInterfaceService);
let questionService = TestBed.get(QuestionManagementService);
let question = new PracticeQuestion(...);
const responseData = { result: 'success', ['additional-info']: 'question added successfully' };
let httpResponseEvent:HttpResponse<any> = new HttpResponse<any>({body:responseData});
//mock response of WebToBackendInterfaceService
spyOn(backendService,'createQuestion').and.returnValue(new Observable(()=>{
httpResponseEvent;
}));
questionService.addQuestion$.subscribe((res:Result)=>{
console.log('received response from Question Services',res);
expect(res).toBeTruthy();
let validResponse:boolean = ((res.result === 'success') || (res.result === 'initial')) ;
expect(validResponse).toEqual(true);
});
questionService.addQuestion(question);
expect(backendService.createQuestion).toHaveBeenCalled();
});
The issue was that I was creating an Observable but I didn't push the value to an Observer using next. The correct implementation is (code snippet)
spyOn(backendService,'createQuestion').and.returnValue(new Observable((subscriber)=>{ //subscriber or observer
subscriber.next(httpResponseEvent)}
));
When creating an Observable, the Observable's constructor in Rxjs takes a subscribe function as argument. The definition of subscribe function is
(observer)=>{
/*logic of calculating values which Observer should produce and emit then using observer.next(value)*/
}
reference - http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html
With the above code, the this.bs.createQuestion(question) returns a mock Observable whose subscribe function is
(subscriber)=>{ //subscribe function
subscriber.next(httpResponseEvent)
}
The above subscribe function will be called whenever any Observer subscribes to the Observable. So in code .subscribe((res)=>{...}, I subscribe to the mock Observable and now the subscribe function will emit the dummy value, httpResponseEvent to my code. For those new to Observables, the subscribe function takes an Observer as argument. An Observer is an object which has 3 methods next, error and complete. Note that the subscribe function takes such an object
(res)=>{... },
(error:ServerResponseAPI)=>{
...
},
()=>{ //observable complete
...
})
I am testing AuthService which sends user login info to server using another HelperService.
public authServiceSigninUser(user:UserSigninInfo):any{
console.log('In authServiceSigninUser. contacting server at '+this.API_URL +this.SIGNIN_USER_URL +" with user data "+user+ " with httpOptions "+httpOptions.withCredentials + ","+httpOptions.headers ); //TODOM password should be sent in encrypted format.
let signinInfo= new UserSigninAPI(user);
let body = JSON.stringify(signinInfo);
return this.helperService.sendMessage(this.SIGNIN_USER_URL,body,httpOptions)
}
I am trying to test the authServiceSigninUser method as follows but when I run the spec, I get error TypeError: Cannot read property 'subscribe' of undefined. It seems that the Observable.Why? The test spec is
describe('authServiceSigninUser test suite',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AuthService, HelperService]
});
});
fit('should sign in user',()=>{
let spy:any;
let helper = TestBed.get(HelperService);
console.log("helper services is ",helper);
let authService = TestBed.get(AuthService);
console.log("auth services is ",authService);
let userSignIn = new UserSigninInfo("test#test.com","test");
let httpMock = TestBed.get(HttpTestingController);
spyOn(helper,'sendMessage');
const responseData = { result: 'success', ['additional-info']: 'login success' };
let httpEvent:HttpResponse<any> = new HttpResponse<any>({body:responseData});
let observable:Observable<HttpEvent<any>> = authService.authServiceSigninUser(userSignIn);
console.log("observable ",observable);//this is undefined
let subscription = observable.subscribe((event)=>{ //error here
console.log('event from authService',event);
});
const mockReq:TestRequest = httpMock.expectOne(environment.apiUrl+environment.signinUserUrl); //Expect that a single request has been made which matches the given URL, and return its mock
//once mocking of sending request is done, mock receiving a response. This will trigger the logic inside subscribe function
mockReq.flush(httpEvent); //flush method provides dummy values as response
httpMock.verify();//verify checks that there are no outstanding requests;
expect(helper.sendMessage).toHaveBeenCalled();
});
});
sendMessage in HelperService is
sendMessage(url:string, body:any,httpOptions):Observable<HttpEvent<any>> {
this.loaderService.show();
let observable:Observable<HttpEvent<any>> = this.http.post<any>(url,body,httpOptions);
return observable.pipe(
tap((httpEvent:HttpEvent<any>) => {//tap transparently perform actions or side-effects, such as logging.
if(httpEvent.type === HttpEventType.Response)
{
console.log('response from backend service:', httpEvent);
}
else {
console.log("not an http response")
}
return httpEvent;
})
,catchError(err=>this.handleError(err))
,finalize(()=> this.loaderService.hide()));
}
I don't know why the above test doesn't work but I think that my approach in general about testing isn't correct. As I am unit testing AuthService, I probably should mock the responses I expect from methods of dependent services. Eg, I should spyOn sendMessage and return a mock value. Also my object should be to test that sendMessage was called with correct arguments.
describe('authServiceSigninUser test suite',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],//need this as HelperSerice needs HttpClient and this module provides it. I could have also used HttpClientModule
providers: [AuthService, HelperService]
});
});
fit('should send request to sign in user',()=>{
// let spy:any;
let helper = TestBed.get(HelperService);
//console.log("helper services is ",helper);
let authService = TestBed.get(AuthService);
//console.log("auth services is ",authService);
let userSignIn = new UserSigninInfo("test#test.com","test"); //data to send to server
//response expected from sendMessage
const responseData = { result: 'success', ['additional-info']: 'login success' };
let httpEvent:HttpResponse<any> = new HttpResponse<any>({body:responseData});
spyOn(helper,'sendMessage').and.returnValue(new Observable(()=>{
httpEvent
})); //when sendMessage is called, return the mocked response
let observable:Observable<HttpEvent<any>> = authService.authServiceSigninUser(userSignIn); //now when authServiceSignInUserr is called, it will call sendMessage and the spyOn will return mocked response
console.log("observable returned ",observable);
let subscription = observable.subscribe((event)=>{
console.log('event from authService',event);
});
//check that the arguments passed to sendMessage are correct
let APIToBeUsed = authService.SIGNIN_USER_URL;
let JSONToBeUsed = JSON.stringify(new UserSigninAPI(userSignIn));
let HTTPOptionsToBeUsed = authService.httpOptions;
expect(helper.sendMessage).toHaveBeenCalledWith(APIToBeUsed,jasmine.any(String),jasmine.any(Object));
expect(helper.sendMessage).toHaveBeenCalledWith(jasmine.any(String),JSONToBeUsed,jasmine.any(Object));
expect(helper.sendMessage).toHaveBeenCalledWith(jasmine.any(String),jasmine.any(String),HTTPOptionsToBeUsed);
});
});