私は、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
などの関数のpromiseを返すことです。したがって、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)
ご覧のとおり、チャイアサーションは実際に失敗しましたが、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);
次のようなカスタム関数を使用します。
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"))
})
})
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...');
}
});
それが私のアプローチです、より良い方法を知りません。