UnitTestライブラリ
劣化expectations+a。
ユースケース。
descはRakeのそれみたいなもんです。
expect_withは検査用メソッド名を渡して呼ぶ。expectは#===で検査する。
expect_eql?みたいなのはmethod_missingでexpect_withに:eql?を渡した
感じに変換される。
自分で使いそうなのは後のふたつだろうか。expect_withはmethod_missing頼りだと演算子なメソッドで検査させられないと気づいてつけたもんだし。
2008/06/09修正: トレース情報の扱いを間違えてて行番号とかがわけわからん
ことになってました。修正。
unit_test do desc "Should raise for #hoge" expect NoMethodError do Object.hoge end expect SyntaxError do Object.piyo end desc "Should match" expect_with :===, /\AA.*?Z\Z/ do "AtoZ" end desc "Should not match because of `eql?'" expect_eql? /\AA.*?Z\Z/ do "AtoZ" end expect_eql? "AtoZ" do "AtoZ" end end
↑の出力結果。
3/5:60.0%:oxoxo C:/home/ruby/lib/unittest.rb:140:=== Expected : SyntaxError Actual : #<NoMethodError: undefined method `piyo' for Object:Class> @ Should not match because of `eql?' C:/home/ruby/lib/unittest.rb:150:eql? Expected : /\AA.*?Z\Z/ Actual : "AtoZ"
unittest.rb
ライブラリ本体。
require 'singleton' module UnitTest class Suite include Singleton def initialize(*args, &block) super @last_description = nil end def cases @cases ||= [] end def desc(*args) @last_description = args.join("\n") end def expect_with(comp, expected, &actual) poping_description do |desc| location = caller(2).find{|b| /:in `(?:method_missing|(expect(?:_.*?)?))'\Z/ !~ b } cases << Case.new(location, expected, actual, desc, comp) end end def expect(expected, &actual) expect_with :===, expected, &actual end def method_missing(name, *args, &actual) super unless /\Aexpect_/ =~ name.to_s expect_with $'.intern, *args, &actual end # # Format: # Successes/Total:Achivement[%]:StatusSequence # # @ Description [&optional] # FileName:Caller:Comparer # Expected : value # Actual : value # def run return unless @cases results = cases.map{|c| c.evaluate } total = results.size ns, nf = results.partition{|r| r }.map{|x| x.size } ox = results.map{|r| r ? "o" : "x" }.join puts "#{ns}/#{total}:#{Float(ns) / total * 100}%:#{ox}\n" puts unless nf.zero? failures = cases.reject{|c| c.evaluate } failures.each do |failure| puts render_failure_case(failure) puts end end end private def poping_description yield @last_description @last_description = nil end def render_failure_case(f) buf = f.description ? "@ #{f.description}\n" : "" location, method = f.location.split(/:in\s*/, 2) method = method ? "#{method[1...-1]}():" : '' buf << "#{location}:#{method}#{f.comp}\n" buf << "Expected : #{f.expected.inspect}\n" buf << "Actual : #{f.actual.inspect}\n" buf end end #class Suite class Case def initialize(location, expected, actual, desc = nil, comp = :===) @location = location @expected = expected @actual = actual @description = desc @comp = comp end attr_reader :location attr_reader :expected attr_reader :description attr_reader :comp def actual @actual_evaluated_cache ||= evaluate_actual() end def evaluate self.expected.send @comp, self.actual end private def evaluate_actual begin @actual.call rescue => ex ex end end end #class Case end #module UnitTest def unit_test(&block) UnitTest::Suite.instance.instance_eval(&block) end at_exit{ UnitTest::Suite.instance.run }
ユニットテストのなんたるかもきちんとわかってないうちからやっちったよ。
これ使えるんのか? 実用に足りてもたぶん自分はexpectations使うよな。
当日記は車輪の最発明を地で往く所存です。