Does Jasmine's toThrow matcher require the argument to be wrapped in an anonymous function?

17,692

Solution 1

We can do away with the anonymous function wrapper by using Function.bind, which was introduced in ECMAScript 5. This works in the latest versions of browsers, and you can patch older browsers by defining the function yourself. An example definition is given at the Mozilla Developer Network.

Here's an example of how bind can be used with Jasmine.

describe('using bind with jasmine', function() {

    var f = function(x) {
        if(x === 2) {
            throw new Error();
        }
    }

    it('lets us avoid using an anonymous function', function() {
        expect(f.bind(null, 2)).toThrow();
    });

});

The first argument provided to bind is used as the this variable when f is called. Any additional arguments are passed to f when it is invoked. Here 2 is being passed as its first and only argument.

Solution 2

Let’s take a look at the Jasmine source code:

try {
  this.actual();
} catch (e) {
  exception = e;
}
if (exception) {
  result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
}

This is the core part of the toThrow method. So all the method does is to execute the method you want to expect and check if a exception was thrown.

So in your examples, fn or thing.doIt will be called in the Jasmine will check if an error was thrown and if the type of this error is the one you passed into toThrow .

Solution 3

Sadly, it seems that if I need to test a function that takes parameters, then I will need to wrap it with a function.

I would rather have preferred,

expect(myTestFunction, arg1, arg2).toThrow();

But I am ok with explicitly doing

expect(function(){myTestFunction(arg1, arg2);}).toThrow("some error");

FYI, note that we can also use a regex match on error:

expect(function (){myTestFunction(arg1, arg2);}).toThrowError(/err/);

Solution 4

If it is used like this:

expect(myTestFunction(arg)).toThrowAnyError(); // incorrect

Then the function myTestFunction(arg) is executed before expect(...) and throw an exception before Jasmine has any chance of doing anything and it crashes the test resulting in an automatic failure.

If the function myTestFunction(arg) is not throwing anything (i.e. the code is not working as expected), then Jasmine would only get the result of the function and check that for errors - which is incorrect.

To alleviate this, the code that is expected to throw an error is supposed to be wrapped in a function. This is passed to Jasmine which will execute it and check for the expected exception.

expect(() => myTestFunction(arg)).toThrowAnyError(); // correct

Share:
17,692
brahn
Author by

brahn

Updated on June 06, 2022

Comments

  • brahn
    brahn about 2 years

    The documentation at https://github.com/pivotal/jasmine/wiki/Matchers includes the following:

    expect(function(){fn();}).toThrow(e);
    

    As discussed in this question, the following does not work, because we want to pass a function object to expect rather than the result of calling fn():

    expect(fn()).toThrow(e);
    

    Does the following work?

    expect(fn).toThrow(e);
    

    If I've defined an object thing with a method doIt, does the following work?

    expect(thing.doIt).toThrow(e);
    

    (If so, is there a way to pass arguments to the doIt method?)

    Empirically the answer seems to be yes, but I don't trust my understanding of JavaScript scoping quite enough to be sure.

  • Ustaman Sangat
    Ustaman Sangat over 12 years
    Sadly, it seems that if I need to test a function that takes parameters, then I will need to wrap it with a function.
  • AlignedDev
    AlignedDev over 11 years
    That works for me. Note: remember the null in f.bind( or your parameters will be one off in the called function. I missed that the first time. (I linked to this to make it easier for me to find :geekswithblogs.net/Aligned/archive/2013/03/21/…) Thanks!
  • Ustaman Sangat
    Ustaman Sangat over 8 years
    Jasmine 2.0 has toThrowError(/regex/)
  • Jan Paepke
    Jan Paepke over 3 years
    For the record, you can do expect(() => myTestFunction(arg1, arg2);}).toThrow(); 1. no need for argument in toThrow 2. less code, if you can do ES6
  • Peter Mortensen
    Peter Mortensen over 2 years
    Something seems to be missing near "called in the" (it is incomprehensible).