问题
I'm struggling to work out the best way to verify that a promise is rejected in a Mocha test while using async/await.
Here's an example that works, but I dislike that should.be.rejectedWith
returns a promise that needs to be returned from the test function to be evaluated properly. Using async/await removes this requirement for testing values (as I do for the result of wins()
below), and I feel that it is likely that I will forget the return statement at some point, in which case the test will always pass.
// Always succeeds
function wins() {
return new Promise(function(resolve, reject) {
resolve('Winner');
});
}
// Always fails with an error
function fails() {
return new Promise(function(resolve, reject) {
reject('Contrived Error');
});
}
it('throws an error', async () => {
let r = await wins();
r.should.equal('Winner');
return fails().should.be.rejectedWith('Contrived Error');
});
It feels like it should be possible to use the fact that async/await translates rejections to exceptions and combine that with Chai's should.throw, but I haven't been able to determine the correct syntax.
Ideally this would work, but does not seem to:
it('throws an error', async () => {
let r = await wins();
r.should.equal('Winner');
(await fails()).should.throw(Error);
});
回答1:
The problem with this approach is that (await fails()).should.throw(Error)
doesn't make sense.
await
resolves a Promise
. If the Promise
rejects, it throws the rejected value.
So (await fails()).should.throw(Error)
can never work: if fails()
rejects, an error is thrown, and .should.throw(Error)
is never executed.
The most idiomatic option you have is to use Chai's rejectedWith
property, as you have shown in your question.
Here's a quick example. Not much is different from what you've demonstrated in your question; I'm just using async
functions for wins()
and fails()
and expect
instead of should
. Of course, you can use functions that return a Promise
and chai.should
just fine.
const chai = require('chai')
const expect = chai.expect
chai.use(require('chai-as-promised'))
// Always succeeds
async function wins() {
return 'Winner'
}
// Always fails with an error
async function fails() {
throw new Error('Contrived Error')
}
it('wins() returns Winner', async () => {
expect(await wins()).to.equal('Winner')
})
it('fails() throws Error', async () => {
await expect(fails()).to.be.rejectedWith(Error)
})
If you like want your wins()
test to resemble your fails()
test more closely, you can write your wins()
test like so:
it('wins() returns Winner', async () => {
await expect(wins()).to.eventually.equal('Winner')
})
The key thing to remember in either of these examples is that chai-as-promised
returns promises for its functions such as rejectedWith
and eventually.something
. Therefore you must await
them in the context of an async
test function, or else failing conditions will still pass:
async function wins() {
return 'Loser'
}
async function fails() {
return 'Winner'
}
it('wins() returns Winner', async () => {
expect(wins()).to.eventually.equal('Winner')
})
it('fails() throws Error', async () => {
expect(fails()).to.be.rejectedWith(Error)
})
If you ran the tests with the code above, you'd get the following:
$ npm test
> mocha-chai-async@1.0.0 test /home/vsimonian/code/mocha-chai-async
> mocha .
√ wins() returns Winner
(node:13836) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rej
ection id: 1): AssertionError: expected 'Loser' to equal 'Winner'
(node:13836) [DEP0018] DeprecationWarning: Unhandled promise rejections are dep
recated. In the future, promise rejections that are not handled will terminate
the Node.js process with a non-zero exit code.
√ fails() throws Error
(node:13836) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rej
ection id: 2): AssertionError: expected promise to be rejected with 'Error' but
it was fulfilled with 'Winner'
2 passing (11ms)
As you can see, the chai assertions actually failed, but they were failed in the context of a Promise that no one ever await
ed or catch
ed. So Mocha sees no failure and marks the tests as though they passed, but Node.js (in behaviour that will change in the future as noted above) prints the unhandled rejections to the terminal.
回答2:
I use a custom function like this:
const expectThrowsAsync = async (method, errorMessage) => {
let error = null
try {
await method()
}
catch (err) {
error = err
}
expect(error).to.be.an('Error')
if (errorMessage) {
expect(error.message).to.equal(errorMessage)
}
}
and then, for a regular async function like:
const login = async (username, password) => {
if (!username || !password) {
throw new Error("Invalid username or password")
}
//await service.login(username, password)
}
I write the tests like this:
describe('login tests', () => {
it('should throw validation error when not providing username or passsword', async () => {
await expectThrowsAsync(() => login())
await expectThrowsAsync(() => login(), "Invalid username or password")
await expectThrowsAsync(() => login("username"))
await expectThrowsAsync(() => login("username"), "Invalid username or password")
await expectThrowsAsync(() => login(null, "password"))
await expectThrowsAsync(() => login(null, "password"), "Invalid username or password")
//login("username","password") will not throw an exception, so expectation will fail
//await expectThrowsAsync(() => login("username", "password"))
})
})
回答3:
You can use async/await
and should
for simple verification
it('should not throw an error', async () => {
try {
let r = await wins();
r.should.equal('Winner');
} catch (error) {
error.should.be.null();
}
});
it('throws an error', async () => {
try {
await fails();
} catch (error) {
error.should.be.Error();
error.should.have.value("message", "Contrived Error");
}
});
回答4:
This is my Solution for the problem .
try {
// here the function that i expect to will return an errror
let walletid = await Network.submitTransaction(transaction)
} catch (error) {
// assign error.message to ErrorMessage
var ErrorMessage = error.message;
// catch it and re throw it in assret.throws fn and pass the error.message as argument and assert it is the same message expected
assert.throws(() => { throw new Error(ErrorMessage) },'This user already exists');
}
// here assert that ErrorMessage is Defined ; if it is not defined it means that no error occurs
assert.isDefined(ErrorMessage);
回答5:
This example only works with Node!
When you use Mocha on Node.js you can use doesNotReject()
or rejects()
both require a function that returns a promise.
Example for when it should reject:
await rejects(testFunction());
see: https://nodejs.org/api/assert.html#assert_assert_rejects_asyncfn_error_message
Example for when it should not reject:
await doesNotReject(testFunction());
see: https://nodejs.org/api/assert.html#assert_assert_doesnotreject_asyncfn_error_message
回答6:
If test your Promised function, in the test must wrap the code inside a try/catch and the expect() must be inside the catch error block
const loserFunc = function(...args) {
return new Promise((resolve, rejected) => {
// some code
return rejected('fail because...');
});
};
So, then in your test
it('it should failt to loserFunc', async function() {
try {
await loserFunc(param1, param2, ...);
} catch(e) {
expect(e).to.be.a('string');
expect(e).to.be.equals('fail because...');
}
});
That is my approach, don't know a better way.
回答7:
A no dependency on anything but Mocha example.
Throw a known error, catch all errors, and only rethrow the known one.
it('should throw an error', async () => {
try {
await myFunction()
throw new Error('Expected error')
} catch (e) {
if (e.message && e.message === 'Expected error') throw e
}
})
If you test for errors often, wrap the code in a custom it
function.
function itThrows(message, handler) {
it(message, async () => {
try {
await handler()
throw new Error('Expected error')
} catch (e) {
if (e.message && e.message === 'Expected error') throw e
}
})
}
Then use it like this:
itThrows('should throw an error', async () => {
await myFunction()
})
回答8:
you can write a function to swap resolve & reject handler, and do anything normally
const promise = new Promise((resolve, rejects) => {
YourPromise.then(rejects, resolve);
})
const res = await promise;
res.should.be.an("error");
来源:https://stackoverflow.com/questions/45466040/verify-that-an-exception-is-thrown-using-mocha-chai-and-async-await