やりたいこと
こんな感じのコードがあった。
// DB操作を伴うとかそういう系のイメージ
const Observable = require("rxjs/Rx").Observable;
const { map, finalize, catchError } = require("rxjs/operators");
function foo (val) {
console.log("foo!");
return val;
}
// connection closeは常にしたいとか
function teardown () {
console.log("teardown!");
}
// エラーが起きたときだけrollbackしたいだとか
function whenError(err) {
console.log("some process...");
throw err;
}
function wantToDo () {
return Observable.from([1, 2, 3]).pipe(
finalize(() => teardown()),
map((val) => foo(val)),
catchError((err) => whenError(err)),
);
}
module.exports = {
wantToDo,
foo,
teardown,
whenError,
};
wantToDo().subscribe((val) => console.log("some logic..."));
wantTodo()関数に対してこんな項目をテストしたくなった。
- 正常系で、最後にteardownが呼ばれたか。
- 異常系でも、最後にteardownが呼ばれたか。
- 異常系のときは、whenErrorも呼ばれたか。
とりあえずテスト書いてみる。
こんなん書きました。
it("should call teardown", () => {
// arrange
sinon.spy(teardown); // 多分これじゃ動かないけど、teardownをスパイしたとして。
// act & assert
wantToDo().subscribe(
(val) => {},
(err) => expect.fail(),
() => expect(teardown.called).to.be.true, // コケる...
);
});
ハマりました。
rxjs/operatorsのfinalizeについて説明を見ると、こう書いてました。
Returns an Observable that mirrors the source Observable, but will call a specified function when the source terminates on complete or error.
(RxJS API Document)
finalizeに書いたものは、Subscriptionがcompleteもしくはerrorで閉じた後に実行されるんですね。
書き直し。
これでちゃんとテストできました。
it("should call teardown", () => {
// ...
// act & assert
wantToDo().subscribe(
(val) => {},
(err) => expect.fail(),
).add(() => expect(teardown.called).to.be.true);
Subscription.add()に書いたteardownメソッドは、finalizeに書いたものよりも後に実行されるようです。
でも問題もあって…。
テストが成功している間は大丈夫でした。
問題はコードを修正してテストが失敗したとき。
mocha -r -w
とかで起動してさぁ修正…と思ったら。
テストに失敗したときにmochaのプロセスが落ちることがあるんですよね。
テスト起動し直すのめんどくせ…ってなりつつ、まだ解決策が見つかってません。
もし知ってる方がいれば教えてください。