DEV Community

Marco Mannucci
Marco Mannucci

Posted on

Use bind to assert a function throws error

If you are using Chai (but that should be valid for other testing frameworks) for your unit tests and you need to assert that a function throws an error, you can use the assert.throws function.

However, the doc is not really clear on how to use it so, if you are testing a function foo to throw an error with input bar you could be tempted to write the test like this (as I initially did):

assert.throws(foo('bar'), "Expected error message");

Turns out this way the test does not work.

Reading more carefully the documentation, we can see that the assert.throws function expects, as its first argument, a function

.throws(fn, [errorLike/string/regexp], [string/regexp], [message])

but in my first attempt I was passing the application of a function.

One possible way to make the test function as it is supposed, is to use the bind method:

assert.throws(foo.bind(foo, 'bar'), "Expected error message");

It will create a new function with the this keyword set to the first argument, in this case the function itself but it could be whatever makes sense. Next arguments will be passed to the new function when it will be actually called, so we can provide the input under test.

Chai can now use the result of the bind function, a new function, to execute the test in the way it is supposed to, passing the input that should verify our assert.

Top comments (3)

Collapse
 
oysmal profile image
Øystein Malt

Another, maybe more intuitive way to do this in ES2016+ is to pass a lambda function calling the function that should throw. By your example:

assert.throws(() => foo('bar'), "Expected error message");

Gives you the same result :) Anyway, nice post! I know this behavior tripped up many people on my team.

Collapse
 
markomannux profile image
Marco Mannucci

Absolutely! I think even a simple anonymous function would work, but in that case the scope may bite you 😂

Collapse
 
oysmal profile image
Øystein Malt

It wouldn’t in this case, as the function tested would retain its scope. It is functionally the same if you intent to bind the function to itself. In fact bind returns a new function (so you get 2 functions here as well), which is good to remember if using it with e.g. React event handlers (referential equality changes) 😊