test_. In a recent discussion with some colleagues, Zak Tamsen and Muness Alrubaie, Zak described his distaste for this common convention. He continued by pointing out that using .NET Attributes was a superior approach for identifying tests.This began a discussion on what a superior approach to writing tests could be when using Ruby. We implemented several examples before deciding on our preference.
testEach implementation contained limitations. In the end we chose to go with a very simple implementation that is similar to how RSpec defines specifications.
def add_returns_the_sum_of_two_numbers
assert_equal 4, add(2, 2)
end
test 'add returns the sum of two numbers'
assert_equal 4 do
add(2,2)
end
test 'add returns the sum of two numbers' doTo accommodate this syntax we added the
assert_equal 4, add(2, 2)
end
test class method to Test::Unit::TestCase.class << Test::Unit::TestCaseAn additional benefit we were able to include in our change was the ability to raise an error when a test was defined with a duplicate name (or description in our case).
def test(name, &block)
test_name = :"test_#{name.gsub(' ','_')}"
raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include? test_name.to_s
define_method test_name, &block
end
end
However, moving to this new style of test definition can cause issues. The first noticeable issue was that TextMate would no longer run individual tests. We quickly fixed this issue by looking at the code in the Ruby Bundle. The fix was to change the regex that selects the method you are testing. The code below shows how you can capture either the previous call to the test class method or the previously defined test method.
File.open(ENV['TM_FILEPATH']) do |f|We also created a TextMate snippet in the Ruby Bundle to help us create tests in our new style. The following code is the snippet for easily creating tests.
f.read.split("\n")[0...n].reverse.each do |line|
if line =~ /^\s*test\s+"([_a-z][_a-z0-9 ]*[\?!]?)"\s+do\s*$/i
print "test_#{$1.gsub(' ', '_')}"
break
elsif line =~ /^\s*def ([_a-z][_a-z0-9]*[\?!]?)/i
print $1
break
end
end
end
test "${1:test name}" do
$0
endAfter these quick changes we are quite happy working with our new format for creating tests.