メタプログラミングなテストは書くな

Jay Fields' Thoughts: Testing Anti-Pattern: Metaprogrammed Tests

ループとdefine_methodでテストメソッドを定義したらだめだという話。

  • 行番号が意味なくなる。
  • 失敗したテストが見つけにくくなる。
  • テストが複雑になりすぎる。そのため、メンテナンス性がなくなってしまう。

こんなのはだめ。

require 'test/unit'

class GradeTests < Test::Unit::TestCase
  (0..100).each do |index|
    letter = case index
      when 0..59 then "F"
      when 60..69 then "D"
      when 70..79 then "C"
      when 80..89 then "B"
      when 90..100 then "A"
    end

    define_method "test_#{index}_is_#{letter}" do
      assert_equal letter, index.as_letter_grade
    end
  end
end

結果をEnumerableで持たせて比較したほうがマシ。全部同じ結果になるのでuniqでひとつにしちゃう。うまいなぁ。

class GradeTests < Test::Unit::TestCase
  def test_numbers_that_are_As
    assert_equal ["A"], (90..100).collect { |int| int.as_letter_grade }.uniq
  end

  def test_numbers_that_are_Bs
    assert_equal ["B"], (80..89).collect { |int| int.as_letter_grade }.uniq
  end

  # ... test the other letters
end


げげっ、rcodetoolsのテストがもろmetaprogrammed testじゃん!なおさなあかんなぁ…