Test for expected failure in Mocha

2019-01-18 11:49发布

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?

9条回答
We Are One
2楼-- · 2019-01-18 11:59

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

expect(Constructor).to.throw(Error);
查看更多
可以哭但决不认输i
3楼-- · 2019-01-18 12:00

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!

查看更多
\"骚年 ilove
4楼-- · 2019-01-18 12:03

Chai now has

should.fail() and expect.fail()

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

查看更多
女痞
5楼-- · 2019-01-18 12:03

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.

查看更多
家丑人穷心不美
6楼-- · 2019-01-18 12:10

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.

查看更多
Luminary・发光体
7楼-- · 2019-01-18 12:11

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.

查看更多
登录 后发表回答