RubyでTest::Baseっぽいのを作る  [ruby]  [TDD]  [sample_code]

Rubyで動的に関数定義3

で書いた方法をつかって、PerlのTest::Baseっぽいのを書いてみる。

(Ruby Test::Baseってのがすでにあるみたいだけどね。)

まずは、test_base.rb

require 'test/unit' 
class TestBase < Test::Unit::TestCase
  @@test_template = {}
  def run(*args)
    return if@method_name.to_s == "default_test"
    super
  end
  def self.start_test
    self.parse if(@@test_template.empty?)
    self.generate_tests 
  end
  private
  def self.parse
    test_name = ""
    buf = ""
    buf_mode = ""
    while line = DATA.gets do 
      line.chomp!
      if(line =~ /^(===|---)/)
        if(buf != "" && test_name != "" && buf_mode != "")
          @@test_template[test_name][buf_mode] = eval(buf)
          buf = ""
        end
        if(line =~ /^=== (.*)$/)
          test_name = $1
          @@test_template[test_name] = {}
        elsif(line =~ /--- input (.*)$/)
          @@test_template[test_name][:method_name] = $1
          buf_mode = :input
        elsif(line = ~/--- expected/)
          buf_mode= :expected
        end
      else
        buf += line
      end
    end
    if(buf != "" && test_name != "" && buf_mode != "")
      @@test_template[test_name][buf_mode] = eval(buf)
      buf = ""
    end
  end
  def self.generate_tests
    @@test_template.each do |test_name,test_args|
      define_method(test_name) do
        result=__send__(test_args[:method_name],test_args[:input])
        assert_equal test_args[:expected], result, " error on #{test_name}"
      end
    end 
  end
end

DATAの処理のところが、ちょっと(かなり?)スパゲッティーだけど、generate_testsの所でテスト用の関数をDATA内容に合わせて作成してる。

Test::Unit::TestCaseのrunメソッドで"default_test"を無視するようにして、TestCaseがテスト未定義時に勝手にdefault_testを実行するのを防いでる。(この部分を抜くとどうなるか試せば言ってる意味がわかるはず)

実際のテストのほうは、こんな感じで書く。

require './test_base'
require './testee' 

class TestBaseTest < TestBase
  def setup #setupとかは通常のTestCaseと同じ
    @testee = Testee.new
  end
  def login_test(login_name) # DATAを利用するテストはtest_以外のメソッド名
    @testee.login(login_name)
    @testee.who_am_i?
  end
  def test_without_data # DATAを使わないテストはtest_で始まるメソッド名
    true
  end
end
TestBaseTest.start_test #最後にstart_testでテストを生成しておく
__END__ #ここからテストに渡すパラメータ
=== test_should_login_by_hoge
--- input login_test #inputの後ろに利用するメソッド名
:hoge
--- expected
:hoge
=== test_should_login_by_fuga
--- input login_test
:fuga
--- expected
:fuga

inputの後ろのメソッド名が一個しかかけなかったり、フィルタとか便利なものがなかったり、"ruby Test::Base"よりかなりはしょってるけど、1ファイルで出来てるのでその辺はしょうがないと割り切る。

じゃあの。