雑文発散

«前の日記(2014-08-15) 最新 次の日記(2014-08-17)» 編集
過去の日記

2014-08-16 [長年日記]

[Emacs] Emacs Lisp のユニットテストツール ERT について調べてみた

自分用にちょっとした Emacs Lisp を書き始めているんだけど、まぁ、これがすんなり書けない。

elisp の Info を読んだり、ググったりしつつ、なんとか関数定義をして、だんだんとそれっぽく動くようになってきた。

その関数の動作確認には *scratch* バッファで C-x C-e (eval-last-sexp) や C-j (eval-print-last-sexp) を実行したり、message を使ったプリントデバッグをやったりしていた。

いろいろ入力パターンを変えて実行していたものの「いや待て、こういう時はユニットテストだろう?」と思い直して、全く理解していなかった Emacs Lisp のユニットテストについて調べ始めた。

どんなものがあるのかも知らなかった(何かの記事とかで読んだ気もするけど記憶に残っていなかった)ので、またしてもググったりして、辿り着いたのが ERT (Emacs Lisp Regression Testing) だった。

ERT は、Emacs 24.1 から標準添付されるようになったそうだし、まぁ、これが今のスタンダードなんだろう。もしかすると、これ以上の機能やら性能やらを持つユニットテストツールがあるのかも知れないけど、まずは標準を知っておくことが重要だろう。

参考にしたのは、この辺の記事。

後者の記事は、前者の記事からも参照されていた。ERT でのテストコードのサンプルが GitHub に置いてあって、大変参考になった。ありがたし。

で、サンプルを見ながら試してみた結果、ようやくテストケースの実行まで辿り着いた。

ERT 実行結果

他のユニットテストのツールを知っていれば、まぁだいたいこの出力の意味は分かると思う。成功・失敗・スキップ・トータルのテストケース数、開始時刻・終了時刻などはすぐ分かる。

緑色のドットがある部分は、プログレスバーになっていて、テストの実行が進む度に更新されていくイメージ。他のユニットテストツールと同じく、ドット1個は「テストケース1個」を表していて、成功時は緑色に失敗時は赤く表示される。

「Selector」というのは、テストケースのフィルタ機能のようなもの。「全部」「◯◯がテスト名に含まれるもの」などといった絞り込みができる。「真」を表す「t」だと「全部」って意味になるそうだ。

Selector は ERT を実行時に選択することができる。M-x ert を実行すると次に Selector の入力が要求されるので、そこで入れてやれば良い。

いくつかある中でも良いなーと思ったのは「:failed」というキーワード。失敗したテストケースだけ選択して再実行ができる。「テスト→失敗→修正→テスト」のループがやりやすいんじゃないかな。まだそこまで実行できてないけど(テスト1個しか書いてないし)。

また、上記の例では Emacs 内でインタラクティブに実行しているけど、コマンドラインで Emacs のバッチモードを使って実行することもできる。

ERT バッチモードで実行

これを実行してみてちょっと不満だったのは、成功・失敗の場合でもテキストに色がつかないこと。ちょっと調べてもオプションなどで制御することはできなさそう。

ちょっと強引に、ert.el の中で定義されている関数(ert-string-for-test-result)を上書きして実行したら、こうなった。失敗時の赤色も出してみたかったので、必ず失敗するテストケースを1個追加してみている。

ERT バッチモードで実行(色付け)

関数のどこを変えたかというと、結果出力に使われる文字列にカラー表示用のエスケープシーケンスを追加した。

(defun ert-string-for-test-result (result expectedp)
  "Return a string that represents the test result RESULT.

EXPECTEDP specifies whether the result was expected."
  (let ((s (cl-etypecase result
             (ert-test-passed '("\e[32mpassed\e[m" "PASSED"))
             (ert-test-failed '("failed" "\e[31mFAILED\e[m"))
             (ert-test-skipped '("skipped" "SKIPPED"))
             (null '("unknown" "UNKNOWN"))
             (ert-test-aborted-with-non-local-exit '("aborted" "ABORTED"))
             (ert-test-quit '("quit" "QUIT")))))
    (elt s (if expectedp 0 1))))

上のコードの「\e[32mpassed\e[m」とか「\e[31mFAILED\e[m」の部分が変更点。その他の部分は、オリジナルのまま。

このコード内で、テスト成功の時は「PASSED」ではなく「passed」が使われ、失敗時は「failed」ではなく「FAILED」が使われているようなんだけど、なんで大文字小文字が入れ替わって使われるのか?とかはまだ良く分かっていない。

まぁ、これだと無理矢理感が強いのと、まだインタラクティブにテストする方法で事足りるので、ここはあまり追求はしなくていいかなー。


【追記】翌日の日記に ERT のチートシート的なものを書いた。