我正在努力找出在使用async / await时在Mocha测试中验证承诺被拒绝的最佳方法。
这是一个有效的例子,但我不喜欢should.be.rejectedWith
返回一个需要从测试函数返回的promise才能正确评估。使用async / await删除了测试值的这个要求(就像我在下面的wins()
的结果一样),我觉得很可能在某些时候会忘记return语句,在这种情况下测试将始终通过。
// 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');
});
感觉应该可以使用async / await将拒绝转换为异常的事实并将其与Chai的should.throw相结合,但我无法确定正确的语法。
理想情况下这可行,但似乎不是:
it('throws an error', async () => {
let r = await wins();
r.should.equal('Winner');
(await fails()).should.throw(Error);
});
这种方法的问题是(await fails()).should.throw(Error)
没有意义。
await
解决了Promise
。如果Promise
拒绝,它会抛出被拒绝的值。
所以(await fails()).should.throw(Error)
永远不会工作:如果fails()
拒绝,则会抛出错误,并且.should.throw(Error)
永远不会被执行。
您最常用的选择是使用Chai的rejectedWith
属性,如您在问题中所示。
这是一个简单的例子。与你在问题中所展示的内容没什么不同;我只是使用async
函数为wins()
和fails()
和expect
而不是should
。当然,您可以使用返回Promise
和chai.should
的函数。
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)
})
如果你想要你的wins()
测试更接近你的fails()
测试,你可以像这样编写你的wins()
测试:
it('wins() returns Winner', async () => {
await expect(wins()).to.eventually.equal('Winner')
})
在这两个例子中要记住的关键是chai-as-promised
返回其函数的承诺,例如rejectedWith
和eventually.something
。因此,您必须在await
测试函数的上下文中对它们进行async
,否则失败条件仍将通过:
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)
})
如果您使用上面的代码运行测试,您将获得以下内容:
$ npm test
> [email protected] 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)
正如你所看到的那样,chai断言实际上已经失败了,但它们在Promise的背景下失败了,没有人会用await
ed或catch
ed。因此,Mocha没有看到任何失败,并将测试标记为好像已经通过,但Node.js(如上所述,将来会改变的行为)将未处理的拒绝打印到终端。
这是我解决问题的方法。
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);
如果测试你的Promised函数,在测试中必须将代码包装在try / catch中,而expect()必须在catch错误块内
const loserFunc = function(...args) {
return new Promise((resolve, rejected) => {
// some code
return rejected('fail because...');
});
};
所以,然后在你的测试中
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...');
}
});
这是我的方法,不知道更好的方法。
我使用这样的自定义函数:
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)
}
}
然后,对于常规的异步函数,例如:
const login = async (username, password) => {
if (!username || !password) {
throw new Error("Invalid username or password")
}
//await service.login(username, password)
}
我写这样的测试:
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"))
})
})