NeverBlog::Likk::Unexistable;

見なかったことにして下さい

forkprove から yath に移行した

Perlでテストを書いてるみなさん。テストランナーは何を使ってますか? 個人プロジェクトではずっと forkprove を使っていましたが、ちょっとした勘違いがきっかけで yath に移行しました

forkprove とは

forkprove は miyagawa さんが作った高速テストランナーです。App::Prove のラッパーで、通常の prove が fork + exec でテストを実行するのに対し、forkprove は fork + eval で実行します。 これにより、-M オプションでプリロードしたモジュールを子プロセスでも再利用でき、モジュールの読み込みコストを削減できるという仕組みです。2012年の Perl Advent Calendar で紹介されていました。

きっかけ

長らく forkprove を使っていたのですが、Perl 5.26 以降との組み合わせで相対パス指定時にテストが失敗するようになりました。

do "t/path/to/test.t" failed, '.' is no longer in @INC;
did you mean do "./t/path/to/test.t"? at (eval 16) line 1.

Perl 5.26 から @INC にカレントディレクトリが含まれなくなった影響です。./t/path/to/test.t のように明示的に ./ を付ければ解決するのですが、当初この問題の原因に気づかず、別のテストランナーで解決できないか調べてしましました。(冒頭の勘違い部分です)

yath(Test2::Harness)とは

yathTest2::Harness が提供するモダンなテストランナーです。

Test2 フレームワークと相性が良く、Test2::Tools::Spec などの Test2 Workflow で書かれた describe/it 構文もちゃんと動きます。 また、TAP 出力を解釈できるので Test::More など従来のテストモジュールでも動作します。 アクティブにメンテナンスされているのも安心ポイント。

forkprove と yath の違い

どちらもプリロードと並列実行をサポートしていますが、思想が少し違います。

forkprove - prove のラッパーとしてシンプルに高速化 - fork + eval による軽量なアプローチ

yath - Test2 エコシステムとの統合を重視 - Persistent Runner(yath start)で常駐プロセスを立ち上げ、さらに高速化可能 - モジュール変更時の自動リロードや、変更されたモジュールのブラックリスト化など、開発中の利便性も考慮

移行

やることはシンプルで、forkproveyath test に置き換えるだけ。

Before

if [ $# -gt 0 ]; then
  forkprove -vr $1
else
  forkprove -j4 -r ./t
fi

After

if [ $# -gt 0 ]; then
  yath test -v $1
else
  yath test -j4 ./t
fi

全テスト実行時は -j4 で4並列にしています。

基本的な使い方

# 全テスト実行
yath test ./t

# 特定のテスト実行
yath test t/path/to/test.t

# 詳細出力
yath test -v t/path/to/test.t

# 並列実行(4並列)
yath test -j4 ./t

結果

移行後、テストが正常に実行できるようになりました。ログの出力フォーマットが変わるので、慣れは必要です。

なお、forkprove も ./ を付ければ Perl 5.26 以降でも問題なく動作します。yath がパス指定の違いを気にしなくて良いのは、forkprove が do を使ってテストファイルを読み込む(@INC に依存)のに対し、yath は perl t/path/to/test.t のようにコマンドライン引数としてファイルパスを渡すため、@INC の影響を受けないからです。どちらを使うかは好みや要件次第です。

forkprove の出力

t/path/to/test.t 指定時(失敗)

do "t/path/to/test.t" failed, '.' is no longer in @INC; 
did you mean do "./t/path/to/test.t"? at (eval 18) line 1.
t/path/to/test.t .. 
No subtests run 

Test Summary Report
-------------------
t/path/to/test.t (Wstat: 0 Tests: 0 Failed: 0)
  Parse errors: No plan found in TAP output
Files=1, Tests=0,  1 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU)
Result: FAIL

./t/path/to/test.t 指定時(成功)

./t/path/to/test.t .. 1/?
./t/path/to/test.t .. ok
All tests successful.
Files=1, Tests=2,  1 wallclock secs ( 0.01 usr  0.01 sys +  0.16 cusr  0.02 csys =  0.20 CPU)
Result: PASS

yath の出力(どちらのパス指定でも成功)

( PASSED )  job  1    t/path/to/test.t

                                Yath Result Summary
-----------------------------------------------------------------------------------
     File Count: 1
Assertion Count: 6
      Wall Time: 0.94 seconds
       CPU Time: 1.46 seconds (usr: 0.19s | sys: 0.04s | cusr: 1.05s | csys: 0.18s)
      CPU Usage: 154%
    -->  Result: PASSED  <--

参考

締め

そもそも prove しか知らず、perl に他のテストランナーがあるとは思ってなかったので、今後は yath 使い続けてみようかなと思うところ。 個人用の小規模プロジェクトなのでサクッと簡単に移行できたけど、プロジェクトの規模が大きかったり、.proverc や Harriet を使い込んでると移行は大変そうなので、簡単に勧められないところがウィークポイントな気がする