tap-reporter でテスト出力をカスタマイズしちゃおう

こんにちはみなさん

テストを走らせている時、皆さんは何を考えているでしょうか。
プロジェクトが大きくなって、テストが増えてくると、一回テストを走らせるだけでも数分、デバッガ使ってカバレッジも出していれば、簡単に10分を超えてしまいます。
実際、私の現在のプロジェクトだと、カバレッジ出すのに13分超えてしまいます。
そんだけ時間がかかっても、一箇所テストが落ちるだけでやり直しなわけで、なかなかの緊張感となるわけです。

そこで、テスト結果の出力をカスタマイズし、豊かなバリエーションとちょっとした遊び心を持って、テスト中の心のゆとりを確保しようというのが今回の目論見なのです！

nyan-catかわいい

# TAP
## Test Anything Protocol
テスト結果の出力をカスタマイズするに当たり、どのように出力されるかの統一規格が必要です。
それがTAP...Test Anything Protocolです。
perlやってる人なら、prove使うときにこんな感じの出力があると思いますが、この出力形式がTAPってやつです。
[ここから引用しています](https://testanything.org/tap-version-13-specification.html)

```
TAP version 13
1..6
#
# Create a new Board and Tile, then place
# the Tile onto the board.
#
ok 1 - The object isa Board
ok 2 - Board size is zero
ok 3 - The object isa Tile
ok 4 - Get possible places to put the Tile
ok 5 - Placing the tile produces no error
ok 6 - Board size is 1
```
要するに、こういうふうに出力されるってわかっていれば、それを自分なりに変えてしまえばいいということです。
また、このTAP形式に対応しているテストフレームワークはいくつもあるようで、例えばPHPUnitも

```
$ phpunit --tap
```
でTAP形式の出力が可能です。
今回は前回やったAVAのネタを引っ張って行こうと思います。

## tap reporter
テスト結果を何らかのスクリプトに食わせてそのスクリプト上で標準出力上にカスタマイズした結果を出力すればいいのですが、自分で作るのは面倒くさいですよね。
そんなときは、偉大な先人たちの力を借りるのみです。
[tap-reporter](https://github.com/sindresorhus/awesome-tap#reporters)
この中から、好きなレポーターを使えば自分の好きな形式で、テスト結果を眺めることができるというわけです。

今回は検証用に、次のようなインチキなテストコードを用意してみました。

```longtime.js
import test from 'ava'

for (let i = 1; i < 50; i++){
  test.serial.only(async t => {
    const timeout = new Promise((res, rej) => {
      if (i == 51) {// ここの値を変えるとエラーを発生させられる
        setTimeout(() => res('ng'), 50)
      } else {
        setTimeout(() => res('ok'), 50)
      }
    })

    t.is(await timeout, 'ok')
  })
}
```
テスト失敗を発生させるための小細工を仕込んであります。
検証用なんで、こんなの実運用に入れちゃだめですぜ。

### tap-dot
phpunitを使っている人であれば馴染みのある、テストが問題なく通ったらドット(`.`)で表現し、間違っていたら、その部分を「x」で表現し、そのエラーの内容を出力します。

インストールは簡単で、

```
# npm install tap-dot --save-dev
```
こんな感じで導入できます。
`package.json`のtestの項目を

```
"test": "ava --tap | tap-spec"
```
としてやると準備完了となります。
というわけで、テストを動かしてみましょう。

```
# npm test
npm info it worked if it ends with ok
npm info using npm@4.2.0
npm info using node@v7.9.0
npm info lifecycle avatest@1.0.0~pretest: avatest@1.0.0
npm info lifecycle avatest@1.0.0~test: avatest@1.0.0

> avatest@1.0.0 test /srv
> ava --tap | tap-dot


  .................................................

  49 tests
  49 passed

  Pass!
npm info lifecycle avatest@1.0.0~posttest: avatest@1.0.0
npm info ok
```
こんな感じの出力です。
テスト失敗したときの挙動はこんな感じです。

```
# npm test
npm info it worked if it ends with ok
npm info using npm@4.2.0
npm info using node@v7.9.0
npm info lifecycle avatest@1.0.0~pretest: avatest@1.0.0
npm info lifecycle avatest@1.0.0~test: avatest@1.0.0

> avatest@1.0.0 test /srv
> ava --tap | tap-dot


  .....................................x...........


  ---
    name: AssertionError
    assertion: is
    operator: ===
    values:
      'Difference:': '"ngok"'
    at: 'Test.<anonymous> (test/longtime.js:13:7)'
  ...



  49 tests
  48 passed
  1 failed

  Failed Tests:   There was 1 failure

    x longtime › [anonymous]

npm info lifecycle avatest@1.0.0~test: Failed to exec test script
npm ERR! Test failed.  See above for more details.
```
失敗時に差分の内容が色分けされなくなるので、若干見づらくはなります。

### tap-spec
mochaのspec出力に似た感じの出力が出せます
ただし、tapという単純な出力形式を経由するので、レポートがエラく縦長になるので、あまりおすすめはしません。

インストールは

```
# npm i tap-spec --save-dev
```
で、`package.json`は

```
"test": "ava --tap | tap-spec"
```
でオッケーです。
失敗を含むテストを実行すると、以下のようになります。

```
# npm test
npm info it worked if it ends with ok
npm info using npm@4.2.0
npm info using node@v7.9.0
npm info lifecycle avatest@1.0.0~pretest: avatest@1.0.0
npm info lifecycle avatest@1.0.0~test: avatest@1.0.0

> avatest@1.0.0 test /srv
> ava --tap | tap-spec


  longtime › [anonymous]

    ✔ longtime › [anonymous]

  longtime › [anonymous]

    ✔ longtime › [anonymous]

( 中略 )

    ✖ longtime › [anonymous]
    -------------------------
      name: AssertionError
      assertion: is
      operator: ===
      values:
        'Difference:': '"ngok"'
      at: 'Test.<anonymous> (test/longtime.js:13:7)'


  longtime › [anonymous]

    ✔ longtime › [anonymous]

( 中略 )

  Failed Tests: There was 1 failure

    longtime › [anonymous]

      ✖ longtime › [anonymous]


  total:     49
  passing:   48
  failing:   1
  duration:  10s


npm info lifecycle avatest@1.0.0~test: Failed to exec test script
npm ERR! Test failed.  See above for more details.
```

### tap-nyan
**これがやりたかっただけ**
テストが走ると、猫が走ります。
例によってインストールは

```
# npm install tap-nyan --save-dev
```
でいけます。
コマンドがちょっと違っていて、`package.json`には

```
"test": "ava --tap | tnyan"
```
こうやって書く必要があります。
テストが走ると猫が走り出します。

```
# npm test
npm info it worked if it ends with ok
npm info using npm@4.2.0
npm info using node@v7.9.0
npm info lifecycle avatest@1.0.0~pretest: avatest@1.0.0
npm info lifecycle avatest@1.0.0~test: avatest@1.0.0

> avatest@1.0.0 test /srv
> ava --tap | tnyan

 49  -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_,------,
 0   -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_|   /\_/\
 0   -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-^|__( ^ .^)
     -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-  ""  ""
  Pass!
npm info lifecycle avatest@1.0.0~posttest: avatest@1.0.0
npm info ok
```
こっちは成功パターン

```
# npm test
npm info it worked if it ends with ok
npm info using npm@4.2.0
npm info using node@v7.9.0
npm info lifecycle avatest@1.0.0~pretest: avatest@1.0.0
npm info lifecycle avatest@1.0.0~test: avatest@1.0.0

> avatest@1.0.0 test /srv
> ava --tap | tnyan

 48  -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_,------,
 1   -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_|   /\_/\
 0   -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-^|__( x .x)
     -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-  ""  ""
  Failed Tests: There was 1 failure

    ✗ # longtime › [anonymous]
: longtime › [anonymous]
npm info lifecycle avatest@1.0.0~posttest: avatest@1.0.0
npm info ok
```
失敗するとこんな感じです。

実際にはこんな感じで動きます
![tapnyan.gif](https://qiita-image-store.s3.amazonaws.com/0/102182/276228cd-f231-94fe-4d38-5d9bc7f869f2.gif)

# まとめ
テストの結果をtap-reporterを使うことでカスタマイズできました。
ここで大事なのは、**tapという標準の形式を通して、出力形式を独自のものにしている**ということです。
つまり、「標準規格を作るって大事だね(小並感)」ということです。

今回はこんなところです。

# 参考
[「同じコード」の同じって何さ - TAPのススメ](http://blog.livedoor.jp/dankogai/archives/51024711.html)
[Awesome TAP](https://github.com/sindresorhus/awesome-tap#reporters)
[tap-nyan](https://github.com/calvinmetcalf/tap-nyan)
[AVA(レポーター)](https://github.com/calvinmetcalf/tap-nyan)