読者です 読者をやめる 読者になる 読者になる

コメント行でUnitテストが出来るようにするスクリプト

http://d.hatena.ne.jp/walf443/20060710/1152551319 のアイディアを部分的に実装してみた。

一時ファイルに出力して云々とかはしてないのでまだちゃんとした体裁にはなっていないのですが。

今のところの問題は

  • クラスが一つしか認識できないこと
  • setup()メソッドやteardown()メソッドが書けない事

といった感じでまだダメダメなんですがなんかレスポンスがもらえるとうれしいので晒しておきます。

id:kikainekoさんのrubytokにインスパイアされて書いたのですが、コードは一から書き直したので全然違うものとなってます。

実際のコードがこんなんで、

class Hoge
  # Test:
  # assert_equal("foo", Hoge.new.foo)
  def foo
    "foo"
  end

  # Test:
  # assert_not_equal("foo", Hoge.new.bar)
  def bar
    "bar"
  end
end

これを実行するとこんな感じに出力されます。

walf443@colinux:doctest% ruby rubytest.rb sample.rb       
require 'test/unit'
require 'sample'

class TestHoge < Test::Unit::TestCase
  def test_foo
    assert_equal("foo", Hoge.new.foo)
  end

  def test_bar
    assert_not_equal("foo", Hoge.new.bar)
  end

end

今のところは出力結果をパイプでrubyに渡してやればテストを実行できます。

walf443@colinux:doctest% ruby rubytest.rb sample.rb | ruby
Loaded suite -
Started
..
Finished in 0.031695 seconds.

2 tests, 2 assertions, 0 failures, 0 errors

ソースはこんな感じです。

#!/usr/bin/env ruby      
                         
unless ARGV.size == 1    
  puts "Usage: ruby #{__FILE__} FILENAME"
  exit                   
end                      
                         
filename = ARGV.shift    
                         
lines = ""               
                         
File.open(filename) do |file|
  lines = file.readlines 
end                      
                         
COMMENT_REGEX = /^#/     
DEFINE_METHOD_REGEX = /^def\s(.+?)\b/
DEFINE_CLASS_REGEX = /^class\s(.+?)\b/
TEST_POINTER_REGEX = /^# Test:\s*$/i
TEST_COMMENT_REGEX = /^#>/
                         
lines = lines.map {|i| i.strip!}.select {|i| (i =~ COMMENT_REGEX ) ||
                          (i =~ DEFINE_METHOD_REGEX) ||
                          (i =~ DEFINE_CLASS_REGEX) }
test_lines = []          
test_lines << "require 'test/unit'\n"
test_lines << "require '#{filename.sub(/\.rb/, "")}'\n"
test_lines << "\n" 
class_lines = []         
method_lines = []        
                         
method_flag = false      
class_flag = true        
class_name = nil         
                         
lines.each do |line|     
  if line =~ DEFINE_CLASS_REGEX
    class_flag = true    
    class_name = "Test#{$1}"
    next                 
  end                    
  if line =~ TEST_POINTER_REGEX
    method_flag = true   
    next                 
  end                    
  if method_flag         
    if line =~ DEFINE_METHOD_REGEX
      method_flag = false
      line = "  def test_#{$1}"
      class_lines << line << method_lines << "  end\n" << "\n"
      method_lines = []
    else                 
      method_lines << line.sub("# ", "    ")
    end                  
  end                    
end                      
                         
class_name ||= "DocTestTestCase"
test_lines << "class #{class_name} < Test::Unit::TestCase\n" << class_lines << "end\n"
                         
puts test_lines         

テストを推奨のクセにTestを全く行なってないことがバレバレなつくりになってます。

メソッドは「def 〜」と出てくるだけでよいのですが、クラスはどういうところで終わりにしていいのかが難しいところです。