on lisp 用のユニットテストで困った件

買ってから放置してあった「On Lisp」を読み始めたんですが、どうせだからサンプルソース用のユニットテストを書いてみようと思ったのが、この話の始まりです。

とりあえずユニットテストフレームワークについて調べ回って、FiveAM がいいんじゃないかという事になりました。
最初の方は大した問題も無く、すらすらと(←一部誇張あり)テストを書けました。
で、コンソールとの入出力を行うユーティリティの章まで進んだところで壁(たぶん Lisper には路肩の段差程度)にぶつかったのです。

他の情報源から、標準入出力を文字列にキャプチャする方法を知ってましたので、このテストコードも余裕だとばかりに書き上げて実行しました。
その結果は無惨にも玉砕でした。そもそも入出力をキャプチャしているはずなのに、しっかりプロンプトが出てキーボード入力を要求したのです。

その後しばらくの間、あーでもないこーでもないと試行錯誤して泣きが入った頃に、ふと「On Lisp」のソースの方を読み返したところ、見事に原因が判明しました。
処理に使っている stream が *standard-{in,out}put* ではなかったのです。
そこには燦然と *query-io* の文字列が輝いていました。

それから更に試行錯誤すること十数分、ようやく目的のコードが出来上がりました。ポイントは一度 input,output の stream を作成してから、それらを make-two-way-stream で合体させて *query-io* に(若干語弊がありますが)上書きする事でした。
動いたソースは以下の通りです。

(5am:test test-prompt
(with-input-from-string (in "3")
(with-output-to-string (out)
(let ((*query-io* (make-two-way-stream in out)))
(5am:is (equal 3 (prompt "Enter a number between ~A and ~A.>> " 1 10) )))
(5am:is (string= (get-output-stream-string out)
"Enter a number between 1 and 10.>> ")) )))

ちなみに、もう一つもトラブったのですが、なんとか下のコードで動きました。ポイントは *standard-output* に不要なテキストが入るので、テスト部分の出力を *error-output* に切り替えておき、そちらの内容をチェックしている事です。

(5am:test test-break-loop
(with-input-from-string (in "(+ 2 3)
:q
")
(with-output-to-string (out)
(let ((*query-io* (make-two-way-stream in out))
(*error-output* (make-string-output-stream)))
(break-loop #'(lambda (x) (format *error-output* "~A" x))
#'(lambda (x) (equal x :q)) ">> ")
(5am:is (string= (get-output-stream-string *error-output*)
"(+ 2 3)"))
))))

…次に使う時には間違い無く忘れてますね、これ。