21

Using Mocha, I am attempting to test whether a constructor throws an error. I haven't been able to do this using the expect syntax, so I'd like to do the following:

it('should throw exception when instantiated', function() {
  try {
    new ErrorThrowingObject();
    // Force the test to fail since error wasn't thrown
  }
  catch (error) {
   // Constructor threw Error, so test succeeded.
  }
}

Is this possible?

OJFord
  • 10,522
  • 8
  • 64
  • 98
bibs
  • 1,226
  • 2
  • 11
  • 14
  • "force mocha test to fail" sounds like you want `expect(false).to.be.true`, but what is actually discussed is testing expected failure, I've requested title edit accordingly. – OJFord Sep 03 '15 at 14:02

9 Answers9

30

should.js

Using the should.js library with should.fail

var should = require('should')
it('should fail', function(done) {
  try {
      new ErrorThrowingObject();
      // Force the test to fail since error wasn't thrown
       should.fail('no error was thrown when it should have been')
  }
  catch (error) {
   // Constructor threw Error, so test succeeded.
   done();
  }
});

Alternative you can use the should throwError

(function(){
  throw new Error('failed to baz');
}).should.throwError(/^fail.*/)

Chai

And with chai using the throw api

var expect = require('chai').expect
it('should fail', function(done) {
  function throwsWithNoArgs() {
     var args {} // optional arguments here
     new ErrorThrowingObject(args)
  }
  expect(throwsWithNoArgs).to.throw
  done()
});
Noah
  • 33,851
  • 5
  • 37
  • 32
  • What if throwsWithNoArgs doesn't return error by itself but just when receiving the wrong params ? and I want to test with different params ? like: **expect(throwsWithNoArguments.call(null, x1, x2)).to.throw(Error)** would be an option ? – alexserver Jan 17 '14 at 17:52
  • 1
    The expect(throws) always calls the `throws` function with no arguments. In your case you could either change the throwsWithNoArgs body to call another function with the incorrect paramemers. Alternatively you could use bind `expect(throwsWithIncorrectArgs.bind(null, x1, x2)).to.throw(Error)` – Noah Jan 17 '14 at 18:27
14

You can try using Chai's throw construct. For example:

expect(Constructor).to.throw(Error);
Mark
  • 39,169
  • 11
  • 42
  • 48
13

Chai now has

should.fail() and expect.fail()

https://github.com/chaijs/chai/releases/tag/2.1.0

Yves M.
  • 29,855
  • 23
  • 108
  • 144
shaunc
  • 5,317
  • 4
  • 43
  • 58
  • um... its either I think? ... you can stick in "to" into the chain if you want, and its more grammatical that way, which is why the small words are provided ("The following are provided as chainable getters to improve the readability of your assertions. They do not provide testing capabilities unless they have been overwritten by a plugin.") – shaunc Jul 01 '15 at 13:01
  • 1
    `expect.fail` is the only static method of `expect` as you can see [here](https://github.com/chaijs/chai/blob/master/lib/chai/interface/expect.js). There are no static chainable getters..`expect.to` does not exists (but `expect(...).to` exists) – Yves M. Jul 01 '15 at 14:20
  • 1
    Ah -- sorry... (I'm a native "should" user). Tks for the correction! – shaunc Jul 01 '15 at 17:54
13

2017 answer if you need to do this with async code: using await and not needing any other libraries.

it('Returns a correct error response when making a broken order', async function(){
  this.timeout(5 * 1000);
  var badOrder = {}
  try {
    var result = await foo.newOrder(badOrder)
    // The line will only be hit if no error is thrown above!
    throw new Error(`Expected an error and didn't get one!`)
  } catch(err) {
    var expected = `Missing required field`
    assert.equal(err.message, expected)
  }
});

Note the poster was only doing sync code, but I expect a lot of people using async were led here by the question title!

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • This doesn't make sense to me. The questions is asking about a constructor, which will always be synchronous so no need for async/await. Next, there is no need to throw an error in the `try`, simply put the assert code in `finally`. – Codebling Feb 16 '18 at 04:06
  • 1
    @CodeBling Sure agreed, the question asker was asking about some sync code, but the title 'testing for expected failure in mocha' has lead a whole bunch of people here who need to test the majority of JS code, ie async, hence the upvotes. – mikemaccana Feb 23 '18 at 18:03
  • That's fair, maybe a downvote was a bit harsh. Still think answer would be more useful if it made it clear that `await` will only help for async tests. Vote is locked in until answer is edited but I'm willing to change it – Codebling Feb 23 '18 at 19:44
9

Mocha in default is using Assert from node.js (https://nodejs.org/api/assert.html). You don't need any external libraries to check if a method throws an error.

Assert has a method - assert.throws, it has three parameters, but only two really matters here:

  • function - here pass function, not function call
  • error - here pass or object constructor or function for checking the error

Let's imagine that you have a function called sendMessage(message) which throws an error when message parameter is not set. Function code:

function sendMessage(message) {
  if (!message || typeof message !== 'string') {
     throw new Error('Wrong message');
  }
  // rest of function
}

Ok, so in order to test it, you need additional function to cover input. Why? Because assert.throws doesn't give any opportunity to pass parameters to the function which going to be tested.

So instead of

// WRONG
assert.throws(sendMessage, Error); // THIS IS WRONG! NO POSSIBILITY TO PASS ANYTHING

you need to create anonymous function:

// CORRECT
assert.throws(() => {
  sendMessage(12);  // usage of wanted function with test parameters
}, Error)

Can you see the difference? Instead of passing function directly, I have put the function call inside anonymous function, in purpose of calling it with a prepared input.

What about the second parameter. It depends from what kind of error should be thrown, in above example Error object was thrown, so I had to put there Error. In result of this action, assert.throws compares if thrown object is object of the same type. If instead of Error something different will be thrown, then this part needs to be changed. For example instead of Error I will throw a value of type String .

function sendMessage(message) {
  if (!message || typeof message !== 'string') {
     throw 'Wrong message'; // change to String
  }
  // rest of function
}

Now the test call

assert.throws(() => {
  sendMessage(12); // usage of wanted function with test parameters
}, (err) => err === 'Wrong message')

Instead of Error in second parameter I have used the comparison function in order to compare thrown error with the expectation.

Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50
5

MarkJ's accepted answer is the way to go and way simpler than others here. Let me show example in real world:

function fn(arg) {
  if (typeof arg !== 'string')
    throw TypeError('Must be an string')

  return { arg: arg }
}

describe('#fn', function () {
  it('empty arg throw error', function () {
    expect(function () {
      new fn()
    }).to.throw(TypeError)
  })

  it('non-string arg throw error', function () {
    expect(function () {
      new fn(2)
    }).to.throw(TypeError)
  })

  it('string arg return instance { arg: <arg> }', function () {
    expect(new fn('str').arg).to.be.equal('str')
  })
})
Andre Figueiredo
  • 12,930
  • 8
  • 48
  • 74
3

If you don't want to wrap a whole lot of source into the expect parameter, or if you have many arguments to pass and it just gets ugly, you can still do this with the original syntax just fine by leveraging the done argument that is provided (but was originally ignored):

it('should throw exception when instantiated', function(done: Done) {
  try {
    new ErrorThrowingObject();
    done(new Error(`Force the test to fail since error wasn't thrown`));
  }
  catch (error) {
    // Constructor threw Error, so test succeeded.
    done();
  }
}

Because you're using done here, it allows you go execute arbitrary code above it in the try, then specify exactly where in your source you'd like to record the failure.

Normally, someone may be tempted to throw or assert(false), but these will both be caught by the catch of the try, and cause you to do some meta-checking to determine if the error you caught was the expected error from your test, or if it was the final determination that your test failed. That's just a mess.

Ian MacDonald
  • 13,472
  • 2
  • 30
  • 51
2

If you are using should.js you can do (new ErrorThrowingObject).should.throw('Option Error Text or Regular Expression here')

If you don't want to should a separate library, you could also do something like this:

it('should do whatever', function(done) {
    try {
        ...
    } catch(error) {
        done();
    }
}

This way, you know the error is caught if the test finishes. Otherwise, you will get a timeout error.

Max
  • 15,157
  • 17
  • 82
  • 127
  • (new ErrorThrowingObject).should.throw('Option Error Text or Regular Expression here') does not work, the new needs to be wrapped in an anonymous function like so: (function() {new ErrorThrowingObject}).should.throw('Option Error Text or Regular Expression here') – Pascal Belloncle Feb 14 '13 at 20:12
  • or you can just do this ErrorThrowingObject.should.throw('Option Error Text or Regular Expression here') – Pascal Belloncle Feb 14 '13 at 20:14
0

With Chai throw (ES2016)

http://chaijs.com/api/bdd/#method_throw

For clarity... This works

it('Should fail if ...', done => {
    let ret = () => {
        MyModule.myFunction(myArg);
    };
    expect(ret).to.throw();
    done();
});

This doesn't work

it('Should fail if ...', done => {
    let ret = MyModule.myFunction(myArg);
    expect(ret).to.throw();
    done();
});
SandroMarques
  • 6,070
  • 1
  • 41
  • 46