この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の9日目の記事です。
9日目 テストについて考える
これまで、テストはなんとなくサンプルプログラムを作って動かしていました。しかしだんだんテストの量も増えてきて、1つ1つコマンドを確認していくのは面倒になってきました。どうにかして簡単にテストを実行したいのですが、あまりRubyのテストについて詳しくないので、簡単にテストを作る方法について調査します。
まず、テストで具体的にどのようなことを行いたいのかを考えます。ビルドツールのテストなのですから、そのコマンドが想定通りに実行されたことを確認したいです。今回定義する関数群は明確な出力が定義されているわけではなく、ターゲットを登録したり、コマンドを実行して外部に何かを投げるだけなのですから、テストをどうやって実行するか悩んでしまいました。
悩んだ結果、とりあえずは標準出力をチェックして、デバッグメッセージやコマンドの実行の様子など、想定するものと同じであればOKとする、というくらいのテストでいいんじゃないかと思い始めました。まあこれは本当火屋くないんだろうけれども。たぶんmake_target
のテストとかは、コマンド実行後に正しくターゲットがリストに挿入されているかを確認する、のようなテストが本当は必要です。しかし、今回はバグがあったところでどうせToyツールなので、あまり気にせずに標準出力だけを考えていきます。
さて、Rubyのテストで標準出力をどのように出得すればよいかを考えたのですが、おそらくSTDOUTを乗っ取る方法が良いです。つまり、RubyのStringIO
を使ってSTDOUTを文字列に格納し、それが想定する文字列と一致しているかどうかを見ようと思います。
$stdout = StringIO.new テストコードを実行 $stdout.string と 想定する文字列が一致するか調査。
という訳で、最初のテストは以下のように改造しました。
#!/usr/bin/ruby require 'test/unit' require 'stringio' class TestExecute < Test::Unit::TestCase ... def test_make_target load "rumy-main.rb" expected_message = "\ [DEBUG] : Target Created = first, Depends = , Commands = [\"echo Hello, First Target\"] echo Hello, First Target Hello, First Target " # replace stdout with string object $stdout = StringIO.new make_target :first do global executes ["echo Hello, First Target"] end exec_target :first assert_equal expected_message, $stdout.string $stdout = STDOUT end ...
test_make_target
テストはmake_target
でコマンドを作成し、exec_target
を実行したときにどのような文字列が出力されるのかを調査するテストです。exepected_message
に想定する出力文字列が格納されており、最初に$stdout = StringIO.new
でSTDOUTを乗っ取り、最後に$stdout = STDOUT
で元に戻します。
今回は、単純にターゲットを作って実行するだけなので、想定する実行結果は、
[DEBUG] : Target Created = first, Depends = , Commands = [\"echo Hello, First Target\"] echo Hello, First Target Hello, First Target
というものになるはずです。これをassert_equal
でチェックしているだけです。これら以外にもテストを3つ用意しました。テストコードを実行してみましょう。
$ RUBYLIB=${PWD}/../src ruby ./exec_test.rb Loaded suite ./exec_test Started ... Finished in 0.4231477 seconds. ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 3 tests, 3 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 7.09 tests/s, 7.09 assertions/s
テストはすべてPassしました。といっても、最初にこのテスト作ったときは新たに導入したglobal
指示子とかが抜けていて余裕で落ちていました。やはりリグレッションテストは大切ですね。
という訳で、とりあえずこれまでに作成したテストはすべてこのUnit Testと標準出力を観察するテストに置き換えました。