RxJS のアプリをテストする


[Testing your Rx application](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/testing.md) の翻訳です。原文のライセンスは Apache 2.0 です。2015年12月31日時点では校正と査読をしている段階です。

現実を見つめましょう。非同期のコードをテストすることは苦痛です。非同期のとても多くのことを調整する JavaScript では、誰もがテストのコードの理解に苦労します。さいわいなことに、JavaScript のための Reactive Extensions はこれをかんたんにします。

長時間に渡って値を publish する observable シーケンスがあるとすれば、リアルタイムのテストはストレッチ可能です。この種の時間に依存したコードがパスするまでの実際の時間を待たなくてすむようにするために、RxJS は TestScheduler 型を提供します。TestScheduler は VirtualScheduler を継承し、エミュレートされる時間で作成、publish、シーケンスの subscribe を可能にします。たとえば、実行に5日間かかる publication を正しい尺度を保って2分に短縮することができます。過去に実際に起きたシーケンスをとり (たとえば、前年の間に ticks する stock のシーケンス) あたかもリアルタイムで新しい値が push out されたように計算もしくは購読することができます。

ファクトリメソッドの startWithTiming、startWithCreate と startWithDispose はキューが空になるまですべてのスケジュールされたタスクを実行します。もしくは queued-up されたタスクが指定された時間までにのみ実行されるように、時間を指定することもできます。

次の例は 指定された onNext 通知をもつ hot observable シーケンスをつくります。それからテストスケジューラーを開始させて、hot observable シーケンスの購読と処分するときを指定します。Start メソッドは Observer のインスタンスを返します。このインスタンスはリストにすべての通知を記録するメッセージプロパティを保有します。
シーケンスが完了した後で、メッセージのプロパティを比較するために `collectionAssert.assertEqual` などのメソッドを定義できます。期待される値のリストと一緒に、両方が等しいことを確認することができます (アイテムは同じ数値をもち、アイテムは等しく同じ順番であること)。このようなことをすることで、期待する通知を本当に受信したことを確認することができます。われわれの例では、150 の時点で購読を開始し、'abc' の値を miss out します。しかしながら、400 の時点で受け取った値を比較するとき、シーケンスを購読してから、publish された値すべてを実際に受け取っています。そして、500 の時点で、OnCompleted 通知が fire されたことも検証しました。加えて、購読の情報も createHotObservable メソッドによって返された Observable 型によってキャプチャされました。

同じ方法で、期待される時間に購読が本当に起きることを確認するために、collectionAssert.assertEqual のような同じ定義のメソ度を使うことができます。QUnit, Mocha, Jasmine、などの好きな Unit テストフレームワークのためのこれを包むことはかんたんです。この例では、QUnit の手軽なラッパーを書きます。

```js
function createMessage(expected, actual) {
  return 'Expected: [' + expected.toString() + ']\r\nActual: [' + actual.toString() + ']';
}

// アサートに QUnit テストフレームワークを使う
var collectionAssert = {
  assertEqual: function (actual, expected) {
    var comparer = Rx.internals.isEqual, isOk = true;

    if (expected.length !== actual.length) {
      ok(false, 'Not equal length. Expected: ' + expected.length + ' Actual: ' + actual.length);
      return;
    }

    for(var i = 0, len = expected.length; i < len; i++) {
      isOk = comparer(expected[i], actual[i]);
      if (!isOk) {
        break;
      }
    }

    ok(isOk, createMessage(expected, actual));
  }
};

var onNext = Rx.ReactiveTest.onNext,
  onCompleted = Rx.ReactiveTest.onCompleted,
  subscribe = Rx.ReactiveTest.subscribe;

test('buffer should join strings', function () {
  var scheduler = new Rx.TestScheduler();

  var input = scheduler.createHotObservable(
    onNext(100, 'abc'),
    onNext(200, 'def'),
    onNext(250, 'ghi'),
    onNext(300, 'pqr'),
    onNext(450, 'xyz'),
    onCompleted(500)
  );

  var results = scheduler.startScheduler(
    function () {
      return input.buffer(function () {
        return input.debounce(100, scheduler);
      })
      .map(function (b) {
        return b.join(',');
      });
    },
    {
      created: 50,
      subscribed: 150,
      disposed: 600
    }
  );

  collectionAssert.assertEqual(results.messages, [
    onNext(400, 'def,ghi,pqr'),
    onNext(500, 'xyz'),
    onCompleted(500)
  ]);

  collectionAssert.assertEqual(input.subscriptions, [
    subscribe(150, 500),
    subscribe(150, 400),
    subscribe(400, 500)
  ]);
});
```

Rx アプリケーションをデバッグする
-----------------------------

Rx アプリをデバッグするために do オペレーターを使うことができます。do オペレーターによって Observable シーケンスのそれぞれのアイテムを取り出すためのさまざまなアクションを指定することができます (たとえば、アイテムを print もしくは log する)。たくさんのオペレーターを連結して、それぞれのレベルで何の値が生成されるのかを知りたいときにとりわけ役立ちます。

Buffer の例を再利用することにします。この例では毎秒ごとに整数を生成する一方で、それらを buffer に　put into します。buffer はそれぞれ5つのアイテムを保有します。Querying Observable Sequences のオリジナルの例では、buffer が full であるときに (そして空になる前に)、最後の　Observable シーケンスだけを購読しました。この例では、しかしながら、オリジナルのシーケンス (毎秒ごとに整数) によって値が push out されているときに、値を print out するために do オペレーターを使います。。buffer が full であるとき、すべてを購読する observer のための最後のシーケンスとして手渡す前に、ステータスを print するために　do オペレーターを使います。

```js
var seq1 = Rx.Observable.interval(1000)
  .do(function (x) { console.log(x); })
  .bufferWithCount(5)
  .do(function (x) { console.log('buffer is full'); })
  .subscribe(function (x) { console.log('Sum of the buffer is ' + x.reduce(function (acc, x) { return acc + x; }, 0)); });

// => 0
// => 1
// => 2
// => 3
// => 4
// => buffer is full
// => Sum of the buffer is 10
// ...
```

ご覧のとおり、連結した observable シーケンスのシリーズの recipient end に購読があります。最初に、Interval オペレーターを使って秒単位で区切られた整数の observable シーケンスをつくります。それから、Buffer オペレーターを使って5つのアイテムを buffer に put して、buffer が full になったときにかぎり、これらのアイテムを別のシーケンスとして send out します。最後に、Subscribe オペレーターに手渡されます。これらすべての中間的なシーケンスは observer に　push されるまで、データによってこれらは propagate down します。同じ方法で、ソースシーケンスの反対方向で購読が propagate されます。このような propagation の真ん中で　do オペレーターを差し込むことで、console.log がデバッグを実行するように、データフローで spy することができます。

observable シーケンスによってアイテムが　push out されるときの時間を検証するために timestamp オペレーターを使うこともできます。正確性を保証するための時間をベースとするオペレーションのトラブルシュートの助けになります。Creating and Subscribing から Simple Observable Sequences までの例を思い出してみましょう。publish されたときまで追加されたソースシーケンスによってそれぞれの値が　push out されるように、timestamp オペレーターをクエリに連結します。このことによって、このソースシーケンスを購読すするときに、アイテムの値とタイムスタンプの両方を受け取ることができます。


```js
console.log('Current time: ' + Date.now());

var source = Rx.Observable.timer(5000, 1000)
  .timestamp();

var subscription = source.subscribe(function (x) {
  console.log(x.value + ': ' + x.timestamp);
});

/* 出力は次のようになる */
// => Current time: 1382646947400
// => 0: 1382646952400
// => 1: 1382646953400
// => 2: 1382646954400
// => 3: 1382646955400
// => 4: 1382646956400
// => 5: 1382646957400
// => 6: 1382646958400
```

timestamp オペレーターを使うことで、5秒のあいだに、最初のアイテムが本当にシーケンスから push out され、それぞれのアイテムが1秒後に publish されることが検証できました。

デバッグを終わらせる前に do と map もしくは select の呼び出しを削除できます。

ロングスタックトレースのサポート
--------------------------

大きな RxJS アプリケーションを扱うとき、デバッグと
どこでエラーが起きたのかを見つけることはむずかしいです。より多くのオペレーターを連結するほど、スタックトレースが長くなり、間違っている場所を見つけることが困難になります。この機能は @kriskowal が開発する Q ライブラリからインスパイアされました。

RxJS には「ロングスタックトレース」に関するオプション機能が用意されています。最新のスタックトレースで止まる代わりに、onError 呼び出しからの Error のスタックプロパティは非同期のジャンプに沿ってトレースするように書き換えられます。例です。

```js
var Rx = require('rx');

var source = Rx.Observable.range(0, 100)
  .timestamp()
  .map(function (x) {
    if (x.value > 98) throw new Error();
    return x;
  });

source.subscribeOnError(
  function (err) {
    console.log(err.stack);
  });
```

エラースタックはかんたんに読みづらくなり、エラーが実際にどこで起きたのかが見つけにくくなります。

```js
$ node example.js

  Error
  at C:\GitHub\example.js:6:29
  at AnonymousObserver._onNext (C:\GitHub\rxjs\dist\rx.all.js:4013:31)
  at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (C:\GitHub\rxjs\dist\rx.all.js:1863:12)
  at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (C:\GitHub\rxjs\dist\rx.all.js:1795:35)
  at AutoDetachObserverPrototype.next (C:\GitHub\rxjs\dist\rx.all.js:9226:23)
  at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (C:\GitHub\rxjs\dist\rx.all.js:1795:35)
  at AnonymousObserver._onNext (C:\GitHub\rxjs\dist\rx.all.js:4018:18)
  at AnonymousObserver.Rx.AnonymousObserver.AnonymousObserver.next (C:\GitHub\rxjs\dist\rx.all.js:1863:12)
  at AnonymousObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (C:\GitHub\rxjs\dist\rx.all.js:1795:35)
  at AutoDetachObserverPrototype.next (C:\GitHub\rxjs\dist\rx.all.js:9226:23)
```

代わりに、次のフラグをセットすることでこの機能を有効にすることができます。

```js
Rx.config.longStackSupport = true;
```

フラグでトップでセットされた状態で、同じコードの例を実行するとき、スタックトレースはより見やすく、エラーがどこで起きたのかを正確に示します。

```js
$ node example.js

  Error
  at C:\GitHub\example.js:6:29
  From previous event:
  at Object.<anonymous> (C:\GitHub\example.js:3:28)
  From previous event:
  at Object.<anonymous> (C:\GitHub\example.js:4:4)
  From previous event:
  at Object.<anonymous> (C:\GitHub\example.js:5:4)
```

これで、6行目でエラーが起きたことがより明確になり、この点のユーザーのコードだけを表示します。これはデバッグにとても役立ちます。さもなければ、最初の一行のみ表示され、ひとかたまりの RxJS 内部のコードが得られるだけで、オペレーションがどこで始まるのかを見わけるための手がかりは得られません。

この機能には深刻なパフォーマンスとメモリのオーバーヘッドが伴いますが、RxJS のたくさんのコードに取り組む場合、もしくは多くのユーザーに対応できるようにサーバーをスケールさせることを試みる場合、オフにしておくべきでしょう。開発において、これらのやっかいなエラーを見つけるために完璧に機能します。

将来のリリースにおいて、かなりかんたんに有効にしたり無効にしたりできるように、node.js の環境変数もサポートする可能性があります。