Search code examples
javascriptunit-testingecmascript-6jasminecycle

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


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.


Solution

  • "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.