15 min/d

ぼうずやのにっき

bouzuya/eater-b-reporter をつくった

bouzuya/eater-b-reporter をつくった。

bouzuya/eater-b-reporteryosuke-furukawa/eater の reporter だ。

bouzuya/eater-b-reporter の目的は eater の reporter の弱さを補うものだ。これは 2016-06-01 にも書いた。 bouzuya/rfc6570-expand の test に yosuke-furukawa/eater を使用した際に感じた eater の reporter の問題点を解決する。

ぼくが eater の標準の reporter (Reporter) で気になる点は 3 つある。

  1. Error の表示が流れてしまう。
  2. Error の表示順序がばらばらだ。
  3. 成功・失敗の件数が分からない。

Error が流れてしまうため、赤い文字に気づいたら scroll して確認しないといけない。極めて面倒だ。test の件数が増えると致命的になる。ご丁寧にすべての file 内の test 別に成否を問わずに出力するため、相当量が流れる。

Error の表示順序がばらばらというのは、複数の file の Error が入り乱れる状態だ。file が child process だから file 順にならない……という程度ならまだいい。eater の reporter は複数の file の Error が入り乱れる。failure の行とその次の行の stack trace が同じ file の Error かさえ分からない。ひどい。

成功・失敗の件数が分からない。これは TapReporter なら良いのだけど (こちらはこちらで……) 、標準の Reporter だと表示されない。どれくらい成功しているのかがつかめないので、あと何件直すのか分からない。先の Error が流れる問題もあって、どこまで scroll すればいいのか分からない状態になる。それも実行するたびだ。

その解決策が bouzuya/eater-b-reporter だ。

次の例は成功時の出力だ。

npm test


> rfc6570-expand@0.1.0 test /home/ubuntu/rfc6570-expand
> beater

19 files
✓ success: .tmp/test/index.js
✓ success: .tmp/test/extended2.js
✓ success: .tmp/test/extended1.js
✓ success: .tmp/test/section3-2-1.js
✓ success: .tmp/test/extended3.js
✓ success: .tmp/test/section3-2-6.js
✓ success: .tmp/test/section3-2-5.js
✓ success: .tmp/test/section3-2-8.js
✓ success: .tmp/test/section3-2-7.js
✓ success: .tmp/test/level2.js
✓ success: .tmp/test/level1.js
✓ success: .tmp/test/extended4.js
✓ success: .tmp/test/negative.js
✓ success: .tmp/test/level3.js
✓ success: .tmp/test/section3-2-9.js
✓ success: .tmp/test/section3-2-3.js
✓ success: .tmp/test/level4.js
✓ success: .tmp/test/section3-2-2.js
✓ success: .tmp/test/section3-2-4.js
✓ 19 files 234 tests completed

copy & paste したものなので分かりづらいが な行は green で表示されている。最初にすべての file 数を表示する。成功した file は名前のみを表示する。最後に file 数と test 数を表示する。成功した file については test() 単位では表示しないため簡素になる。また最後の行で file 数や test 数が表示されるので、きちんと完了したことが分かる。

次の例は失敗時の出力だ。

19 files
✓ success: .tmp/test/extended3.js
✓ success: .tmp/test/extended1.js
✓ success: .tmp/test/extended2.js
✓ success: .tmp/test/extended4.js
✓ success: .tmp/test/index.js
✓ success: .tmp/test/level2.js
✓ success: .tmp/test/level4.js
✓ success: .tmp/test/negative.js
✓ success: .tmp/test/section3-2-1.js
✓ success: .tmp/test/section3-2-3.js
✓ success: .tmp/test/section3-2-4.js
✓ success: .tmp/test/section3-2-5.js
✓ success: .tmp/test/section3-2-6.js
✓ success: .tmp/test/section3-2-7.js
✓ success: .tmp/test/section3-2-8.js
✓ success: .tmp/test/section3-2-9.js
✗ failure: .tmp/test/level1.js
AssertionError: {hello} / Hello%20World%21 / Hello%20World%s21   # .tmp/test/level1.js:12

  assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`)
         |    |                                                            
         |    false                                                        
         ["Hello%20World%21"]                                              

    at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20)
    at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17)
    at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30)
    at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32)
    at runner_1.test.resolve (.tmp/test/level1.js:12:9)
    at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26)
    at emitNone (events.js:86:13)
    at EventEmitter.emit (events.js:185:7)
    at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18)
    at process._tickCallback (internal/process/next_tick.js:103:7)

✗ failure: .tmp/test/level3.js
AssertionError: {x,hello,y} / 1024,Hello%20World%21,768 / 1024,Hello%20World%s21,768   # .tmp/test/level3.js:12

  assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`)
         |    |                                                            
         |    false                                                        
         ["1024,Hello%20World%21,768"]                                     

    at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20)
    at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17)
    at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30)
    at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32)
    at runner_1.test.resolve (.tmp/test/level3.js:12:9)
    at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26)
    at emitNone (events.js:86:13)
    at EventEmitter.emit (events.js:185:7)
    at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18)
    at process._tickCallback (internal/process/next_tick.js:103:7)

✗ failure: .tmp/test/section3-2-2.js
AssertionError: {hello} / Hello%20World%21 / Hello%20World%s21   # .tmp/test/section3-2-2.js:14

  assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`)
         |    |                                                            
         |    false                                                        
         ["Hello%20World%21"]                                              

    at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20)
    at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17)
    at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30)
    at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32)
    at runner_1.test.resolve (.tmp/test/section3-2-2.js:14:9)
    at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26)
    at emitNone (events.js:86:13)
    at EventEmitter.emit (events.js:185:7)
    at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18)
    at process._tickCallback (internal/process/next_tick.js:103:7)
AssertionError: {x,hello,y} / 1024,Hello%20World%21,768 / 1024,Hello%20World%s21,768   # .tmp/test/section3-2-2.js:14

  assert(uris.some(u => u === uri), `${ template } / ${ uris } / ${ uri }`)
         |    |                                                            
         |    false                                                        
         ["1024,Hello%20World%21,768"]                                     

    at Decorator._callFunc (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:110:20)
    at Decorator.concreteAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorator.js:103:17)
    at decoratedAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/lib/decorate.js:49:30)
    at powerAssert (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/empower-core/index.js:63:32)
    at runner_1.test.resolve (.tmp/test/section3-2-2.js:14:9)
    at EventEmitter.nextTest (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:22:26)
    at emitNone (events.js:86:13)
    at EventEmitter.emit (events.js:185:7)
    at checkPromise.then (/Users/bouzuya/.ghq/github.com/bouzuya/rfc6570-expand/node_modules/eater/lib/runner.js:26:18)
    at process._tickCallback (internal/process/next_tick.js:103:7)

✗ 3 of 19 files, 4 of 234 tests failed
npm ERR! Test failed.  See above for more details.

reporter とは関係ないが Error 表示は power-assert-js/power-assert を使っていることもあって詳細だ。

Error は最後に集中して表示する。流さない。Error の表示順序はファイルごとに並んでいる。最後に全体のうちいくつの file, test が失敗したのかを表示している。安心感がある。

さきに挙げた 3 つの問題を解決していることが分かる。

  1. Error の表示が流れてしまう。
  2. Error の表示順序がばらばらだ。
  3. 成功・失敗の件数が分からない。

bouzuya/eater-b-reporter の実装は例によって TypeScript だ。eater は JavaScript なので、型は自分で用意した。

reporter の API に関しては Qiita に記事を書いたので、そちらを参照すると良い。 link は eater の reporter の API - Qiita だ。

つくる過程で Node.js の bug を踏んだのだけど、これについてはまた日を改める。

bouzuya/eater-b-reporter の実例を見たいなら bouzuya/rfc6570-expand で試してみるといい。

$ git clone https://github.com/bouzuya/rfc6570-expand
$ cd rfc6570-expand/
$ npm i
$ npm test

適当に test が通らないようにすれば失敗例も確認できる。