Find duplicate test names in ruby test::unit files

I’ve recently found a couple of instances of tests with the same test name in the same test::unit TestCase class. This is a bad thing, because the when the ruby interpreter reads the file in, it will ignore all methods by the same name apart from the last one, which is the one it will use. Some code to explain:

require 'test/unit'
class MyTest < Test::Unit::TestCase
  def test_me
    puts "aardvark"
  end
  def test_me
    puts "zebra"
  end
end

If you run this code, you’ll get “zebra” printed to the console, not “aardvark”, because the last method definition for test_me is the one that is used.

You’ve probably worked out how this can be a very bad thing in testing – if in a TestCase class you have several test methods by the same name (copy-and-paste mistakes), you’ve got tests which don’t get executed! Then, as happened to me, you’ll get a dev coming over to you asking why your tests didn’t find a particular bug – the test which would have found the bug was never run, that’s why!

To find all instances of duplicate test names, I wrote the code below. To use it, save it to a file, set the root_test_folder variable to point to your root testing folder, and run it. If it finds duplicate tests, it’ll print out the file containing them, the duplicate test names, and how many times the test name is defined in the offending file.


root_test_folder = "/Users/nat/dev/my_testing_project/test"

unless File.exist?(root_test_folder) && File.directory?(root_test_folder)
  abort("You haven't specified a directory in root_test_folder")
end

all_test_files = []

Dir.glob(File.join(root_test_folder,'**/*.rb')).each do |file|
  f = File.new(file)
  all_test_files << file if f.read =~ /class\s+.*\s+< Test::Unit::TestCase/
end

all_test_files.each do |test_file|
  test_names = []
  test_file_contents = File.new(test_file).read

  test_file_contents.each_line do |line|
    test_names << line.strip.gsub(/^(\s)*def\s+/,"") if line =~ /^(\s)*def\s+test_[\d\w]+(\s)*$/
  end

  has_duplicate_test_name = false

  b = test_names.inject(Hash.new(0)) {|h,i| h[i] += 1; h }
  b.each {|dupe_test_name,count| has_duplicate_test_name = true if count > 1 }

  if has_duplicate_test_name
    puts test_file
    b.each {|dupe_test_name,count| puts "#{count}x #{dupe_test_name}" if count > 1 }
    puts ""
  end
end

NB:
- it assumes that each test file only contains one TestCase class
- it has very little error checking and is quite inefficient – but it does the job
- if you have any enhancements, please leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>