Test code inside an rxjs subscription when using jasmine marbles - javascript

I want to be able to test the code that runs inside an observable subscription:
function foo(someStream$: Observable<number>) {
someStream$.pipe(
map((x) => x + 3),
).subscribe((result) => {
SomeService.someFunc(result)
})
}
For example, I want to make sure someFunc() gets called with result. I would think that I should be able to do something like
const someStream$ = cold('-a--', { a: 5 })
const someFuncSpy = spyOn(SomeService, 'someFunc')
foo(someStream$)
expect(someFuncSpy).toHaveBeenCalledWith(8)
Problem is that I will be told that someFunc was never called. I do not want to separate the stream and the subscribe into separate functions.

you could use Rx.Observable.of(5) instead of cold() to pass a number through the observable

Related

Test if an actual function was called with Jest

I am simply trying to test if a function was called using jest 26.6.3. I do not want to mock the implementation of the function. I want to test if my actual function was called.
const add = (a,b) => a + b;
test("add() was called", () => {
add(1,2);
expect(add).toHaveBeenCalled();
})
If I create a mock function, I don't understand how that would be testing to see if my REAL function has been invoked. Obviously, the mock function will be executed if I create it using jest.fn() and then invoke it. Would I have to spy on my add function? Still missing the fundamental understanding to do this simple task.
You will need to spy the function add to get some infos about her like when its called... Then you are to be able to do the expect.
You can do this with jest.spyOn.

Mocking multiple calls

I'm using ts-mock-imports and I want to mock multiple return values of a function call, then check that a function was called a given number of times. The following works:
const getUserSpy = dataServiceMock.mock('getUserFromDB')
.returns({...USER_1})
.returns({...USER_2});
In my test case, I have two calls to getUserFromDB() and with the above, getUserSpy.callCount is equal to 2. But this is order-dependent, so I'd rather be able to do the following:
const getUser1Spy = dataServiceMock.mock('getUserFromDB')
.withArgs(USER_1_ID).returns({...USER_1});
const getUser2Spy = dataServiceMock.mock('getUserFromDB')
.withArgs(USER_2_ID).returns({...USER_2});
What I would expect is that getUser1Spy is called once, and getUser2Spy is called once. However, it looks like the second call to withArgs() removes the previous instance of mocking.
This worked:
const getUserSpy = dataServiceMock.mock('getUserFromDB');
getUserSpy.withArgs(USER_1_ID).returns({...USER_1});
getUserSpy.withArgs(USER_2_ID).returns({...USER_2});
// ... then ...
assert(getUserSpy.callCount).equals(2);

Dynamically build a function

Is it possible to dynamically build a function based off another functions parameters?
For example:
Base Function:
const someFunction = (functionName, functionParams) => {
// Function Built here
};
someFunction("colorFunction", ["red", true]);
I'd like it to build something similar to this: I'd need to deconstruct the Array into individual params, but I'm not sure how simple that is? And I have no idea how I'd use the first String to call the function name?
functionName(...functionParams);
Which in my head would sort of work like this:
const colorFunction = (color, bool) => {
console.log("Colour: " + color);
console.log("Bool: " + bool);
};
Bit confused by this - I feel like I'm not a million miles away, but I'm not certain! Any help would be great, thanks!!
Edit - Why?
I have a react component with a click event that fires off a redux action. Ideally this action would fire some stuff over to my reducer, and asynchronously call this "dynamic" function. I can do this with a load of if/elses, but I don't think that's a very clean way of achieving this, if building a function this way is possible.
First you need to determine where the functions you want to call dynamically are stored. If they are global functions then you can call them using window:
const someFunction = (functionName, functionParams) => {
window[functionName]();
};
If they are methods of an object, then you can do something similar using the object:
const someFunction = (functionName, functionParams) => {
myObject[functionName]();
};
As for how to pass the arguments, you have a couple options here. If you are running a recent version of JS, or using polyfills, then you can indeed use the spread operator:
const someFunction = (functionName, functionParams) => {
window[functionName](...functionParams);
};
Otherwise you can always rely on the apply method:
const someFunction = (functionName, functionParams) => {
window[functionName].apply(null, functionParams);
};
The first argument in the apply method is the context you wish to pass to your function, in your case it doesn't seem necessary, hence the null value.
Edit: corrected bind with apply as mentionned by Bergi
What you are looking for is Function#bind in order to make a thunk - basically a function that takes no parameters and it's used for delayed computation.
Using .bind you can do a partial application on a function. In your case, you would just apply all arguments and only leave the execution step at the end:
//your function
const colorFunction = (color, bool) => {
console.log("Colour: " + color);
console.log("Bool: " + bool);
};
//partially apply all argument to it
const thunk = colorFunction.bind(null, "red", true);
//this can now be passed around and executed at a later point
setTimeout(thunk, 3000);
console.log("wait 3 seconds");
Since functions are first class members in JavaScript, you can pass any function this way. If you really need a function that turns others into thunks then you can very easily do that:
const toThunk = (func, args) => func.bind(null, ...args);
Which is your someFunction but with just the name and parameters re-named for a bit of clarity.

Why is this destructuring not working?

I am creating an observable like so:
return new Observable(sub => {
const {next, complete, error} = sub;
this.AuthHttp.get(`http://192.168.1.100:3000/api/users/${id}`)
.subscribe(res => {
let user = res.json();
next(user);
complete();
}, e => {
error(e.json());
});
})
Yet it nothing is happening in my front end when next() is expected to be called. If I make a minor change to the code so that sub.next() is called instead, everything works as expected. This indicates the underlying code is not flawed, just the way I am making a reference to next.
I have seen this form of destructuring used with the Observer class before (in an example online), so what am I doing wrong here?
Because the next, error and complete methods are object methods that must be called on an object instance.
When you use destructuring to obtain the functions and later call those functions, the calls are without context.
You cannot do what you've attempted for the same reason that this will not work:
const { toString } = new Date();
console.log(toString());
For more information, see this issue.

Need clarification for map and observable syntax in Angular

There is a certain syntax that is baffling me and i see it with the map function and also with the observable in typescript/Angular (Angular 5). I have two methods:
This one is in a component:
logout() {
this.authService.logout().subscribe(
result => {
this.router.navigate(['/login']);
}
);
}
And this is in the related service:
logout(): Observable<any> {
return this.http.post('/api/auth/logout', { }).map(
response => {
this._token = null;
//more unrelated code...
return true
}
);
}
The part that is confusing me in both of these cases is this:
thing => {
//code
}
What is this? The code above works. but I see that have both 'result' and 'response' for thing. Can 'thing' be anything at all or is it defined somewhere?
Also, I looked up the map function in javascript at w3schools (because I've never had a use for it) and it shows in the example that the first parameter is supposed to be a function which gets applied to each element of the array that it is associated with but "thing => {}" is not a function so this is super confusing.
Note, that I have worded my question in such a way as to get to the underlying misunderstanding rather than focusing on my specific problem, however solving my specific problem may help illustrate my misunderstanding.
The problem with the code above is that while it works it does not know what to do when the api endpoint returns a 500 error. I am trying to determine how to catch the error so that I can do something with that on the front end.
Thing can be whatever you want to name it. Result, data, response, etc. Doesn't matter. What you're basically doing is creating a variable for the result emitted from your subscription. The subscription takes in a function() and inside that function, you pass the variable name that you want to be used for the success result. And really, here, using result is meaningless, since nothing is ever done with it. If you aren't going to do anything with the response, its better to just say:
logout() {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
});
}
To catch errors, you only need to pass a comma after the last curly, like so:
logout() {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
}, err => {
// Do something with error here
});
}
As for map, here is an example
var array1 = [1, 4, 9, 16];
const map1 = array1.map(x => x * 2);
It basically takes every variable in the array and performs that map method, meaning it takes each value and does whatever the function says to do, in this case, multiply it by 2. Think of it as a sort of transformation. In that example, it's basically being used to manipulate the response before sending it back to the subscription.

Categories

Resources