Promise 用 Chai アサーション
Chai as Promised は、Chai を拡張し、Promise に関する事実をアサートするための流暢な言語を提供します。
Promise の fulfilled と rejected ハンドラに手動で期待値を接続する代わりに
doSomethingAsync().then(
function (result) {
result.should.equal("foo");
done();
},
function (err) {
done(err);
}
);
本当に意味するところを表すコードを記述できます。
return doSomethingAsync().should.eventually.equal("foo");
または、return
が好ましくない場合(例:スタイル上の考慮事項)、または不可能な場合(例:テストフレームワークが非同期テストの完了を知らせるために Promise の返却を許可していない場合)、次の回避策を使用できます(ここで done()
はテストフレームワークによって提供されます)。
doSomethingAsync().should.eventually.equal("foo").notify(done);
注意:Promise アサーションでは、return
または notify(done)
のいずれかを必ず使用する必要があります。これは、プロジェクトまたはチームで使用されている既存のアサーション形式からわずかに逸脱する可能性があります。それらの他のアサーションは同期的な可能性が高く、特別な処理は必要ありません。
使用方法
should
/ expect
インターフェース
Chai as Promised によって提供される最も強力な拡張機能は、eventually
プロパティです。これにより、既存の Chai アサーションを Promise で動作するアサーションに変換できます。
(2 + 2).should.equal(4);
// becomes
return Promise.resolve(2 + 2).should.eventually.equal(4);
expect({ foo: "bar" }).to.have.property("foo");
// becomes
return expect(Promise.resolve({ foo: "bar" })).to.eventually.have.property("foo");
いくつかの Promise 特有の拡張機能もあります(通常の expect
同等物も利用可能です)。
return promise.should.be.fulfilled;
return promise.should.eventually.deep.equal("foo");
return promise.should.become("foo"); // same as `.eventually.deep.equal`
return promise.should.be.rejected;
return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw` assertion work too.
assert
インターフェース
should
/ expect
インターフェースと同様に、Chai as Promised は chai.assert
に eventually
エクステンダを提供し、既存の Chai アサーションを Promise で使用できるようにします。
assert.equal(2 + 2, 4, "This had better be true");
// becomes
return assert.eventually.equal(Promise.resolve(2 + 2), 4, "This had better be true, eventually");
そして、もちろん、Promise 特有の拡張機能もあります。
return assert.isFulfilled(promise, "optional message");
return assert.becomes(promise, "foo", "optional message");
return assert.doesNotBecome(promise, "foo", "optional message");
return assert.isRejected(promise, Error, "optional message");
return assert.isRejected(promise, /error message regex matcher/, "optional message");
return assert.isRejected(promise, "substring to search error message for", "optional message");
進行状況コールバック
Chai as Promised は、Promise の進行状況コールバックのテストを本質的にサポートしていません。テストしたいプロパティは、おそらく Sinon.JS のようなライブラリ、おそらく Sinon–Chai と組み合わせて使用するのが最適です。
var progressSpy = sinon.spy();
return promise.then(null, null, progressSpy).then(function () {
progressSpy.should.have.been.calledWith("33%");
progressSpy.should.have.been.calledWith("67%");
progressSpy.should.have.been.calledThrice;
});
出力 Promise のカスタマイズ
デフォルトでは、Chai as Promised のアサーションによって返される Promise は、入力 Promise から派生した単一の then
メソッドで拡張された通常の Chai アサーションオブジェクトです。この動作を変更するには、たとえば、ほとんどの Promise ライブラリにあるような、より便利な糖衣構文メソッドを持つ Promise を出力するには、chaiAsPromised.transferPromiseness
をオーバーライドできます。Q の finally
と done
メソッドを転送する例を以下に示します。
import {setTransferPromiseness} from 'chai-as-promised';
setTransferPromiseness(function (assertion, promise) {
assertion.then = promise.then.bind(promise); // this is all you get by default
assertion.finally = promise.finally.bind(promise);
assertion.done = promise.done.bind(promise);
});
アサータへの引数の変換
Chai as Promised が許可するもう1つの高度なカスタマイズフックは、アサータへの引数を非同期で変換したい場合です。おもちゃの例を以下に示します。
import {transformAsserterArgs} from 'chai-as-promised';
setTransformAsserterArgs(function (args) {
return args.map(function (x) { return x + 1; });
});
Promise.resolve(2).should.eventually.equal(2); // will now fail!
Promise.resolve(3).should.eventually.equal(2); // will now pass!
変換は非同期でもかまいません。配列ではなく、配列に対する Promise を返します。その例としては、Promise.all
を使用して、Promise の配列を配列に対する Promise に変換することが挙げられます。そうすれば、アサータを使用して Promise を他の Promise と比較できます。
// This will normally fail, since within() only works on numbers.
Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));
setTransformAsserterArgs(function (args) {
return Promise.all(args);
});
// But now it will pass, since we transformed the array of promises for numbers into
// (a promise for) an array of numbers
Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));
互換性
Chai as Promised は、Promises/A+ 仕様 に準拠したすべての Promise と互換性があります。
特に、jQuery 3.0 より前の jQuery の Promise は仕様に準拠しておらず、Chai as Promised はそれらと動作しません。特に、Chai as Promised は、then
の標準的な 変換動作 を広く使用しており、jQuery<3.0 はこれをサポートしていません。
Angular の Promise は、処理に特別なダイジェストサイクルを持っており、Chai as Promised と連携するには追加のセットアップコードが必要です。
Promise に対応していないテストランナーとの連携
一部のテストランナー(例:Jasmine、QUnit、または tap/tape)には、返された Promise を使用して非同期テストの完了を知らせる機能がありません。可能であれば、Mocha、Buster、または blue-tape のような、それをサポートするテストランナーへの切り替えをお勧めします。しかし、それが不可能な場合でも、Chai as Promised は対応できます。テストフレームワークが非同期テストの実行が終了したことを示すコールバックを受け入れる限り、Chai as Promised は notify
メソッドを使用してその状況に適応できます。
it("should be fulfilled", function (done) {
promise.should.be.fulfilled.and.notify(done);
});
it("should be rejected", function (done) {
otherPromise.should.be.rejected.and.notify(done);
});
これらの例では、条件が満たされない場合、テストランナーは "expected promise to be fulfilled but it was rejected with [Error: error message]"
または "expected promise to be rejected but it was fulfilled."
の形式のエラーを受け取ります。
notify
の別の形式があり、Promise が完了した後にアサーションを実行する場合などに役立ちます。例を以下に示します。
it("should change the state", function (done) {
otherState.should.equal("before");
promise.should.be.fulfilled.then(function () {
otherState.should.equal("after");
}).should.notify(done);
});
.notify(done)
が Promise アサーションの後ではなく、.should
に直接ぶら下がっていることに注意してください。これは、Chai as Promised に、充足または拒否をテストフレームワークに直接渡すように指示します。したがって、上記のコードは、promise
が拒否された場合、Chai as Promised エラー("expected promise to be fulfilled…"
)で失敗しますが、otherState
が変化しない場合は、単純な Chai エラー(expected "before" to equal "after"
)で失敗します。
async
/ await
と Promise 対応テストランナーの使用
Promise を待つ必要があるアサーションは Promise 自体を返すため、async
/ await
を使用でき、テストランナーがテストメソッドから Promise の返却をサポートしている場合は、テストでアサーションを待つことができます。多くの場合、await
の後に同期アサーションを実行することで、Chai as Promised をまったく使用せずに済む場合がありますが、rejectedWith
を待つ方が、Chai as Promised を使用せずに try
/ catch
ブロックを使用するよりも多くの場合便利です。
it('should work well with async/await', async () => {
(await Promise.resolve(42)).should.equal(42)
await Promise.reject(new Error()).should.be.rejectedWith(Error);
});
複数 Promise アサーション
複数の Promise でアサーションを実行するには、Promise.all
を使用して複数の Chai as Promised アサーションを組み合わせます。
it("should all be well", function () {
return Promise.all([
promiseA.should.become("happy"),
promiseB.should.eventually.have.property("fun times"),
promiseC.should.be.rejectedWith(TypeError, "only joyful types are allowed")
]);
});
これにより、個々の Promise アサーションの失敗が "expected promise to be fulfilled…"
メッセージにラップされるのではなく、テストフレームワークに渡されます。 return
を使用できない場合は、前の例と同様に .should.notify(done)
を使用します。
インストールと設定
Node
npm install chai-as-promised
を実行して開始します。その後
import * as chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);
// Then either:
const expect = chai.expect;
// or:
const assert = chai.assert;
// or:
chai.should();
// according to your preference of assertion style
もちろん、このコードを共通のテストフィクスチャファイルに配置できます。 Mocha を使用した例については、Chai as Promised のテスト自体を参照してください。
他の Chai プラグインを使用する場合の注意:Chai as Promised は、インストール時に、現在登録されているすべてのアサータを検出して Promise 化します。したがって、アサータを Promise 化する必要がある場合は、他の Chai プラグインの後で、Chai as Promised を最後にインストールする必要があります。
Karma
Karma を使用している場合は、付属の karma-chai-as-promised プラグインを確認してください。
ブラウザ/Node 互換性
Chai as Promised は、ES モジュールと最新の JavaScript 構文のサポートが必要です。ブラウザがこれをサポートしていない場合は、Babel などのツールを使用してトランスパイルする必要があります。