FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

DSLでビルドツールを自作する (9日目 テストについて考える)

この記事は「Qiita Advent Calendar 2019 DSLで自作ビルドツールを作ろう」の9日目の記事です。

9日目 テストについて考える

これまで、テストはなんとなくサンプルプログラムを作って動かしていました。しかしだんだんテストの量も増えてきて、1つ1つコマンドを確認していくのは面倒になってきました。どうにかして簡単にテストを実行したいのですが、あまりRubyのテストについて詳しくないので、簡単にテストを作る方法について調査します。

まず、テストで具体的にどのようなことを行いたいのかを考えます。ビルドツールのテストなのですから、そのコマンドが想定通りに実行されたことを確認したいです。今回定義する関数群は明確な出力が定義されているわけではなく、ターゲットを登録したり、コマンドを実行して外部に何かを投げるだけなのですから、テストをどうやって実行するか悩んでしまいました。

悩んだ結果、とりあえずは標準出力をチェックして、デバッグメッセージやコマンドの実行の様子など、想定するものと同じであればOKとする、というくらいのテストでいいんじゃないかと思い始めました。まあこれは本当火屋くないんだろうけれども。たぶんmake_targetのテストとかは、コマンド実行後に正しくターゲットがリストに挿入されているかを確認する、のようなテストが本当は必要です。しかし、今回はバグがあったところでどうせToyツールなので、あまり気にせずに標準出力だけを考えていきます。

さて、Rubyのテストで標準出力をどのように出得すればよいかを考えたのですが、おそらくSTDOUTを乗っ取る方法が良いです。つまり、RubyStringIOを使って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と標準出力を観察するテストに置き換えました。