2006年7月 5日

[ruby-list:42515] ちょっとしたテスト

るびきちです。

1つのファイルですむレベルの短いRubyスクリプトをTDDするとき、
みなさんどうしていますか?

10分で書き上げるスクリプトのためにわざわざ別のファイルにわけてテストを
かくのは面倒ですね。かといって if __FILE__ == $0 中にテストを書こうにも、
実装メソッドとテストメソッドの距離がはなれてしまうので嫌です。それに

ちょっとしたスクリプトのテストに「おまじない」を書くのも嫌です。なので
rbtestというスクリプトを作成してみました。

rbtest script-file-name
で埋め込まれたテストを実行できます。

rbtestはRubyスクリプトを読み、
=begin test_foo
などのbeginブロックを発見したとき、
test_fooをテストメソッドにした一時テストスクリプトを作成、実行します。
実行後必ず一時テストスクリプトは消去されます。

さらにemacsのnext-errorで捕獲できるよう工夫してあります。
* 一時テストスクリプトのファイル名を実行スクリプトのファイル名に書き換える。
* 行番号を揃えておく。

#### 実行スクリプト
#!/usr/bin/env ruby
def f(x)
x+1
end
=begin test_f
# wrong test
assert_equal 120, f(11)
=end

=begin
This block is ignored!
=end

def g(x)
x**2
end

=begin test_g
assert_equal 4, g(2)
assert_equal 9, g(3)
=end

if __FILE__==$0
puts f(3)
end

#### rbtestにより生成された一時テストスクリプト
#!/usr/bin/env ruby

require 'test/unit';load 'sample1.rb';class TestByRbtest < Test::Unit::TestCase;def test_f
# wrong test
assert_equal 120, f(11)
end

def test_g
assert_equal 4, g(2)
assert_equal 9, g(3)
end


end

#### 実行結果
Loaded suite ./sample1.rb__test__
Started
F.
Finished in 0.012425 seconds.

1) Failure:
test_f(TestByRbtest) [./sample1.rb:7]:
<120> expected but was
<12>.

2 tests, 3 assertions, 1 failures, 0 errors



#!/usr/bin/env ruby
# -*- ruby -*-

# Usage:
# rbtest script [Test::Unit options]
# rbtest script output # output temporary test script

# preamble
def first_test(script_filename)
"require 'test/unit';" +
"load '#{script_filename}';" +
"class TestByRbtest < Test::Unit::TestCase;"
end

def script_to_test_script(ruby_source, script_filename)
_SKIP = "\n"
state = :start
ruby_source.map do |line|
case line
when /^#!/
line
when /^=begin (test\S*)$/ # test method block
preamble = (state == :start) ? first_test(script_filename) : ""
state = :test_method
preamble + "def #{$1}\n"
when /^=begin/ # other block
state = :begin
_SKIP
when /^=end$/
if state == :test_method
state = :script
"end\n" # /def test_xxx
else
_SKIP
end
else
if state == :test_method
line
else
_SKIP
end
end
end.join + "end\n" # /class TestByRbtest
end

def execute_test_script(test_script_filename, argv)
output = `#{test_script_filename} #{argv[1..-1].join ' '} `
print output.gsub(Regexp.new(Regexp.quote(test_script_filename)), argv.script_filename)
end

def generate_test_script(script_filename, test_script_filename)
open(test_script_filename, "w") do |f|
f.write(script_to_test_script(File.read(script_filename), script_filename))
f.chmod 0755 # executable
end
end

################
def do_test(argv)
begin
test_script_filename = "#{argv.script_filename}__test__.rb"
generate_test_script(argv.script_filename, test_script_filename)
execute_test_script(test_script_filename, argv)
ensure
File.unlink test_script_filename
end
end

def output_test_script(argv)
print(script_to_test_script(File.read(argv.script_filename), argv.script_filename))
end

class << ARGV
alias :script_filename :first
end

if __FILE__ ==$0
if ARGV[1] == "output"
action = :output_test_script
else
action = :do_test
end

__send__(action, ARGV)
end


--
rubikitch
http://www.rubyist.net/~rubikitch/

投稿者 xml-rpc : 2006年7月 5日 11:28
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/42443
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。