Python標準ライブラリに組み込まれた各種の DocTest Rubyインプリメンテーションは、1年前からTom Locke(リンク)、Roger Packによって(リンク)利用されるようになり、その後Dr Nicも(リンク)追随した。
DocTestを使用すれば、標準のインタープリタシェル(IRB for Ruby)からの出力に基づいて容易にテストを生成し、docstringにカットアンドペーストすることが可能である。
次のように IRB テスト出力を関数のコメントとしてコピー/ペーストする。
# doctest: Add 5 and 5 to get 10# >> five_and_five
# => 10
def five_and_five
5 + 5
end
後でテストを実行することができる。
$ rubydoctest five.rb
=== Testing 'five.rb'...
OK | Add 5 and 5 to get 10
1 comparisons, 1 doctests, 0 failures, 0 errors
これは、gitリポジトリからの
(リンク)最新のソースを使用して実行される。最新のgemはまもなく利用可能になる。
このようなdocstring主導型のテストのプラス面としては、
- IRBライブテストに精通している場合には、利用価値があり、使いやすい。
- テストを一箇所で確認できる。
- 単純である。
... マイナス面としては、
- 大規模なdocstringは避け、個別のテストファイルに置く必要がある。
- 複雑なテストアサーションが利用できない場合がある。
- 大規模な出力のテストが面倒である。
Duane Johnsonは(リンク)、バージョン1.0を(リンク)利用可能するために Tom Lockeのリポジトリに変更をマージした。
DocTestの使用法を実演するスクリーンキャストが(リンク)利用できる。
InfoQは、Duane Johnson氏にインタビューし、DocTestとdocstring主導型テストについて聞いた。
InfoQ: 最初に、なぜDocTestを作成したのですか? 前の2つのインプリメンテーションを考察されたそうですが。
Duane Johnson: わたしは現在、テストとドキュメンテーションがほとんど含まれていない大きなRubyプロジェクト(memorypress.com
(リンク))の改良に取り組んでいます。このプロジェクトをクリーンアップグレードするには、意図せずプログラムを破損することがないように、ある種のテストを実施する必要があります。問題は、作業内容とそれらのテストへの取り組み方が必ずしも明確でないために、わたしが「テスト」にかける大半の時間がirbコンソールで費やされることです。つまり、テストは、わたしにとって試験的な作業であり、動作方法を正式に定義すると同時に動作方法を確認する必要があるのです。Ruby DocTestは、成功した試験結果をコードベースにコピーアンドペーストして、完成したテストをチェックすることができるため、完全なコンビネーションです。
github(リンク)で最新のコードが見つかりますか? それはDocTestの前のバージョンとのマージですか? 何を追加されましたか?
ええ、最新のコードです。今週始めに、わたしはgithub.com/tablatom/rubydoctestで
(リンク)Tom Lockeのプロジェクトから分岐して、許可を得て彼のプロジェクトにわたしの変更内容をマージしました。Tomのコードは大変読みやすく(Pythonのdoctestsのように直接文字列をテストするのではなく)、evalを使用して結果を比較する彼のアプローチが有力であることがわかりました。この方法では、Ruby の順序不同ハッシュが正常に比較されます。また、Rubyソースファイルの中のコメント内にdoctestを置くRoger Packのアイディアも採り入れました。(code.google.comにある
(リンク))Rogerのコードは借用していませんが、アイディアは採用しています。その結果、Ruby DocTestは、Rogerのパッケージの "インラインdoctest"スタイルと、Tomのパッケージの"マークダウンファイル内の個別のドキュメンテーション"スタイルの両方の選択肢を提供しています。
すべてのRubyテスティングフレームワークに満足していますか?
何とも言えません。今でもRSpecを相当気に入っています。ですが、わたし自身のワークフローでは、別のファイルに切り替えて(またはファイルを作成して)、テストを作成する作業が遅れがちになるので、つねに作成された実際のコードに取り組むようにしています。しばしば、取り組んでいるメソッドのいくつかのエッジケースをテストする必要があるという些細な思いが生じますが、そのときあまりにも多くの懸案事項が念頭にあるため、やり過ごします。irbセッションをそこに加えれば、わたしのニーズの90%は満たされると思います。ですからそうしました。
はい、そのとおりです。これはテスト先行型のアプローチではありません。先に述べたとおり、わたしの現在のニーズは、テストが不十分な既存のプロジェクトの文書作成にあります。
DocTestは他のユニットテストフレームワークの置換になると考えられますか、それとも追加されるツールにすぎないのでしょうか?
正直なところ、まだわかりません。わたしにとって、DocTestはテスト作成の新しい方法なので、答えが出るまでもうしばらく模索する時間が必要です。わたしの感触では、前に述べた 90%の目標に近づいているように思います。おそらく、一部には従来のRSpecまたは Test::Unitケースに依存する方が妥当な場所があるでしょう
DocTestのコードに注釈を付ける方法は、読みやすさを損ないませんか?
doctestの移行パスは次のようになると思います。
1. コードを作成した後、インラインの doctest コメントを追加する。
2. 他のコード変更を行った後、元のコードに戻る。
3. テストケースをさらに追加するが、テストの数(または長さ)は扱いにくくなるため、それらを個別の *.doctestファイルに移す。
4. 最終的に、指示とコメントが含まれたドキュメンテーションとしてのコード例付きのほぼ完全なマークダウンファイルに近づく。
コードと並行してテストを作成する場合、各Ruby ソースファイルを独立させる傾向が強まるという面白い副次的効果があります。これは、doctest で要求されるように、依存性エラーなしにファイルを "eval" できるようにするための措置です。いったん(Railsファイルの場合のように)隠された仮定から構成される複雑なネットワークからファイルを解放すると、その後テストが容易になり、各ファイルが依存している対象を認識しやすくなります。