How to test the result of a curried function in Jasmine? - javascript

Use Case:
I have a module of functions, each function is unit tested
I have a factory function that creates a stream of these functions that a third party library requires.
I would like to test that this factory function produces the correct stream. Using #cycle/Time, I am able to create the stream and assert on the stream.
I am able to assert that the functions appear on the stream in the correct order.
However, I am unable to assert on any function that is curried. How would one assert on curried functions?
Currently, I have a hack in place to JSON.stringify the functions and assert on their source.
To simplify the problem, I created a simple test suite so we aren't concerned with using #cycle/Time. It appears that curried functions are new instances of the function. Please see the code below.
I was wondering how would I be able to make the failing test pass? In this case I simulate the curried function by using bind. Is this possible?
const a = () => b
const b = () => {}
const c = (arg) => b.bind(null, arg)
const d = () => () => {}
describe("curried function test", function() {
it('should return a reference to b', () => {
expect(a()).toBe(b)
})
// This test fails because b.bind returns a new function.
it('should return a reference to a curried b', () => {
expect(c('foo')).toBe(b)
})
it('should create a new instance everytime', () => {
expect(d()).not.toBe(d())
})
});
I've setup a jsfiddle here.

"This test fails because b.bind returns a new function."
That's because what you get from c is the result from b.bind(null, arg), which isn't the same as b.
Otherwise, b.bind would be modifying b.
As mdn says:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
(source, emphasis mine)
Basically, c can't return a reference to b.
What you do have, is the resulting function's name:
const b = () => {};
const c = (arg) => b.bind(null, arg);
const e = c("foo");
console.log(e.name);
console.log(e.name === `bound ${b.name}`);
So, you could test that e.name equals "bound " + b.name.

Related

How can I "recursively" stringify a javascript function which calls other scoped functions?

Because javascript functions are not serializable, in order to pass them into new contexts sometimes (albeit rarely) it can be useful to stringify them then re-evaluate them later like:
const foo = () => { // do something }
const fooText = foo.toString()
// later... in new context & scope
const fooFunc = new Function(' return (' + fooText + ').apply(null, arguments)')
fooFunc() // works!
However, if foo references another function bar, the scope is not stringified, so if bar is not defined in the new context, the evaluated foo function will throw an error when called.
I'm wondering if there is a way to stringify a function recursively?
That is, not only stringifying the parent function, but also stringifying the contents of the child functions called from the parent.
For Example:
let bar = () => { alert(1) }
let foo = () => { bar() }
// what toString does
let fooString = foo.toString()
console.log(fooString) // "() => { bar() }"
// what we want
let recursiveFooString = foo.recursiveToString()
console.log(recursiveFooString) // "() => { alert(1) }"
Let me know if you have any ideas on how to accomplish something like a "recursiveToString"
The only good way to do this is to start from a parent scope that encloses all functions foo eventually references. For example, with your foo and bar, if you want to pass foo into another context such that bar is callable as well, pass a function that declares both foo and bar, and returns foo. For example:
const makeFoo = () => {
let bar = () => { alert(1) }
let foo = () => { bar() }
return foo;
};
const makeFooStr = makeFoo.toString();
// ...
const makeFooFunc = new Function(' return (' + makeFooStr + ').apply(null, arguments)');
const foo = makeFooFunc();
foo();
Implementing this sort of thing well does require premeditated design like above (unfortunately). You can't really include all ancestor LexicalEnvironments (the internal map of variable names to values in a given scope) when stringifying.
I'm wondering if there is a way to stringify a function recursively?
I think we can fairly simply demonstrate that this is impossible in general.
Let's think about these two function
const greet = (greeting) => (name) => `${greeting} ${name}`
const sayHi = greet ('Hi')
sayHi ('Jane') //=> "Hi Jane"
While with your foo and bar example, we could possibly imagine something that examined the body of the function and used everything available in the current scope to do your extended stringify function based on parsing the function and knowing what local variables are actually used. (I'm guessing that this would be impossible too, for reasons related to Rice's Theorem, but we can certainly imagine it.)
But here, note that
sayHi.toString() //=> "(name) => `${greeting} ${name}`"
and so sayHi depends on a free variable that's not stored in our current scope, namely, greeting. We simply have not stored the "Hi" used to create that function anywhere except in the closure scope of sayHi, which is not exposed anywhere.
So even this simple function could not be reliably serialized; there seems little hope for anything more complex.
What I ended up rolling with was inspired by #CertainPerformance's answer.
The trick is to build a function which defines all the child callee functions. Then you have everything you need to stringify the parent function.
Note: to allow for imported callee functions from other files, I decided to programmatically build a string with the callee definitions rather than defining them originally in the same scope.
The code:
// original function definitions (could be in another file)
let bar = () => { alert(1) }
let foo = () => { bar() }
const allCallees = [ bar, foo ]
// build string of callee definitions
const calleeDefinitions = allCallees.reduce(
(definitionsString, callee) => {
return `${definitionsString} \n const ${callee.name} = ${callee.toString()};`;
},
"",
);
// wrap the definitions in a function that calls foo
const fooString = `() => { ${calleeDefinitions} \n return foo(); \n }`;
console.log(fooString);
/**
* fooString looks like this:
* `() => {
* const bar = () => { alert(1) };
* const foo = () => { bar() };
* return foo();
* }`
**/
// in new context & scope
const evaluatedFoo = new Function(' return (' + fooString + ').apply(null, arguments)');
// works as expected
evaluatedFoo();

Curried JavaScript - calling

I am admittedly a beginner with JavaScript, but I need to piece together someone else's code and I am having trouble understanding the following function and how to call it in node.
const invoke = method => object => object[method]
This obviously is a function that takes a method that returns another function that takes an object that then returns object[method], but what exactly is the purpose? How would one use this? Thank you.
the way i see it is as the const states, it invokes a function stored in an object ,
as you mentionned, this can be broken down to :
const invoke = (method) => {
return (object) => {
return object[method] ;
}
}
the goal of this i believe is that you can call it ( like you're telling a story ) expressively and concisely : invoke the function a from the functions object. ( functionnal-programming )
from this article about functional programming
Functional programming is declarative rather than imperative, and
application state flows through pure functions. Contrast with object
oriented programming, where application state is usually shared and
colocated with methods in objects.
but the term invoke got me thinking about Immediately invoked functions, so the usage of the const invoke can be :
getting function from the object ( without executing it ) not to have to instantiate the whole object and having the function in a variable and maybe manipulate it's prototype.
calling the function ( with parenthesis ).
getting a property from an object.
immediately invoke a function in an object.
const myFns = {
'a' : function(x){
console.log(x || 'something to log when no params passed');
},
'b': {
username : 'Doe'
}
}
const invoke = method => object => object[method]
let myFunction = invoke('a')(myFns);
myFunction('hello from myFunction'); // call it or modify myFunction.prototype ... etc.
invoke('a')(myFns)('hello'); // simply call it
let user = invoke('b')(myFns); // get a property
console.log(user.username);
(invoke('a')(myFns))(); // immidiatly invoke the function
probalby to avoid eval() :P
The name 'invoke' suggests it should really be written like this:
const invoke = method => object => object[method]()
The () is the invocation. It's very general, so hard to say exactly how it would be used, but here's a silly example.
class Dog {
speak () { console.log('woof') }
}
var dogs = [ new Dog(), new Dog(), new Dog() ];
dogs.forEach( invoke( 'speak' ) );
-> 'woof'
-> 'woof'
-> 'woof'
It's a pretty common pattern to let an array method like forEach do the second call of a higher-order function like this.

How can I stub an anonymous function in Sinon?

The following:
const sinon = require('sinon')
const a = () => { return 1 }
sinon.stub(a)
throws TypeError: Attempted to wrap undefined property undefined as function.
stub works if there is an object, so I tried using this. In node.js REPL (v6.11):
> const a = () => { return 1 }
undefined
> this.a
[Function: a]
However, in my mocha spec, it fails:
const a = () => { return 1 }
console.log(a)
// => [Function: a]
console.log(this.a)
// => undefined
What am I missing? How can I make this work?
BTW: I'm aware that I can stub a method of an object, like this: const stub = sinon.stub(object, 'a'), but that's not what I'm after here with this question.
You can't make it work like this. For stubbing, Sinon requires a "root object" because it needs to replace the function reference that you want to stub in that root object. The this in the REPL only works because of how the REPL is implemented. In latest node (v8), it no longer automatically binds functions to this like described.
sinon.stub takes in an object and then you can stub the properties. So you should be able to do
const obj = {
a: (() => return 1; })
};
and then be able to call
const stub = sinon.stub(obj, "a");
As you witnessed, you set const a to be a function in your example -- it needs to be an object and then sinon can stub a specific property in that object. I believe the reason for this is it then gives it something sinon can reference hence why sinon can also support things like object.method.restore().
Another workaround is to bind to this on your own (although that's not recommended):
const a = () => { return 1 }
this.a = a;
sinon.stub(this, 'a').returns(2);
console.log(this.a());
// => 2

Unexpected variables in arguments[] when passing a function

I have two files in a folder - index.js and util.js with their code base as follows
Util.js
let obj = {}
obj.sendTransaction = () => {
console.log(arguments);
return new Promise((resolve, reject) => {
// try {
// let data = ethFunction.call()
// resolve(data)
// } catch (e) {
// reject(e)
// }
});
}
module.exports = obj
In Index.js, if I pass arguments to addNewParticipant or its variation then they do not turn up in the arguments object in util.js, for instance
const addNewParticipant = (foo, bar) => {
var ethFunction = myContract.addParticipant.sendTransaction
console.log(ethFunction);
EthUtil.sendTransaction()
}
const addNewParticipantTwo = (foo, bar) => {
var ethFunction = myContract.addParticipant.sendTransaction
console.log(ethFunction);
EthUtil.sendTransaction(ethFunction, foo, bar)
}
and call it such addNewParticpant(1, 2) and , addNewParticpantNew(1, 2) the numbers 1 and 2 do not show up in the arguments object in the util function. In fact, the arguments object remains the same, 4 inputs describing some functions and files in node_modules including Bluebird and a reference to index.js itself
My final aim is to
Pass a function from index.js to util.js
Pass along unknown number of variables
Call the passed function and apply the unknown number of variables to it
Wrap the whole thing in a promise and do some data validation
Ideally arguments[0] would represent a function I would pass and the other would be the values. I would then use
var result = arguments[0].apply(null, Array().slice.call(arguments, 1));
If it helps, the function I want to pass has an optional callback feature
As already mentioned in the comment, fat arrows don't have their own this or arguments objects. The arguments object you're logging is from the function created by the module loader, and its passed arguments.
You can either use a "regular function", or in this case, you can use a ...rest parameter
And, avoid the Deferred antipattern.
//first a little utility that might be handy in different places:
//casts/converts a value to a promise,
//unlike Promise.resolve, passed functions are executed
var promise = function(value){
return typeof value === "function"?
this.then( value ):
Promise.resolve( value );
}.bind( Promise.resolve() );
module.exports = {
sendTransaction(fn, ...args){
return promise(() => fn.apply(null, args));
}
}

Jasmine SpyOn on function calls

Hello I got a question regarding mocking JS code with Jasmine.
Imagine having the following situation:
function Test(){
var a = 5;
var b = 3;
Test2(a,b);
}
function Test2(a,b){
var result = a + b;
console.log("result of function Test2: ", result);
}
I want to mock my Test2 function call with Jasmine. I tried the following thing:
describe("1# Test Mocking", function () {
it("test: Mocking Example", function () {
var myMock = new Test();
spyOn(myMock, "Test2").and.returnValue(10,10);
expect(Test2.result).toEqual(20);
});
});
But Jasmine keeps saying: Error: Test2() method does not exist
Does anyone knows why this is and how I could solve this?
Your code doesn't make a lot of sense I'm afraid:
you're telling Jasmine to spy on a method of myMock called Test2, yet it doesn't have such a method (as Jasmine is telling you); Test2 is just a regular function;
even if it did had a method called Test2, you're spying on it after new Test(), at which point the original Test2 would already have been called and the spy would be declared too late;
the original Test2 doesn't return a value, yet you are telling the spy that it should return 10, 10 (it seems to me that you want to call it with those two values, not have it return them);
Test2 doesn't have any side-effects (like returning a value or setting an instance variable or something) apart from creating a local variable (result) and logging it, which makes it pretty untestable;
I think you need to go back to the drawing board to formulate what exactly it is you want the class to do. To help you on your way, here's a possible implementation, including a test to see a) if Test2 gets called with the proper arguments and b) if its return value gets stored properly (again, I don't know what you want the class to do, so I'm just providing some examples):
function Test() {
var a = 5;
var b = 3;
this.result = this.Test2(a, b);
}
Test.prototype.Test2 = function(a, b) {
var result = a + b;
return result;
}
describe("1# Test Mocking", function () {
it("test: Mocking Example", function () {
spyOn(Test.prototype, 'Test2').and.returnValue(20);
var myMock = new Test();
expect(myMock.Test2.calls.argsFor(0)).toEqual([ 5, 3 ]);
expect(myMock.result).toEqual(20);
});
});

Categories