LoginSignup
40
35

More than 5 years have passed since last update.

ある日、目が覚めたらRails(のテスト)が壊れていた

Posted at

<この記事は「Money Forward Advent Calendar 2015」の12日目の記事です>

Railsでアプリを書いたり、RubyでRailsを書いたり(主にBug Fix)しています。金子です。

前書き

RailsのCIではRailsのmasterとRubyのtrunkでテストが回っています。普段、趣味でこのテストを眺めていて、テストが壊れたりすると原因を調べてFixしたりしています。

RubyにもRailsにも毎日新しい機能が入っていますので、それらが衝突して思わぬバグが生まれることもあります。今年見ていて面白かったバグを3つほど紹介したいと思います。

第3位 突然の大量Error

スクリーンショット 2015-12-24 10.57.56.png

例えばテスト結果はこちら

  1) Error:
BelongsToAssociationsTest#test_with_select:
ArgumentError: wrong number of arguments (given 0, expected 1)
    /home/travis/build/rails/rails/activerecord/lib/active_record/relation/spawn_methods.rb:41:in `instance_exec'
    /home/travis/build/rails/rails/activerecord/lib/active_record/relation/spawn_methods.rb:41:in `merge!'
    /home/travis/build/rails/rails/activerecord/test/cases/associations/belongs_to_associations_test.rb:349:in `test_with_select'

突然引数の数が足らないと怒られるようになってしまいました。
このエラーはRubyの2.3で新しくHash#to_procが導入されたことによります。
Hash#to_procは以下のようにHashを1引数の関数(引数をkeyとして、それに対応するvalueを返す関数)に変換するためのメソッドです。

def test_to_proc
  h = {
    1 => 10,
    2 => 20,
    3 => 30,
  }

  assert_equal([10, 20, 30], [1, 2, 3].map(&h))
end

Railsのmerge!メソッドでは以下のように引数がto_procを実装しているときに、instance_execに渡していたので、Ruby側の変更でテストが盛大に壊れていました。

def merge!(other) # :nodoc:
  if !other.is_a?(Relation) && other.respond_to?(:to_proc)
    instance_exec(&other)
  else
    klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
    klass.new(self, other).merge
  end
end

このケースではRails側を修正して対応しました。

新しいメソッドが追加されたことで、gemのテストが通らなくなる瞬間に立ち会えたのが印象的でした。

第2位 突然のSyntaxError

スクリーンショット 2015-12-24 10.58.08.png

例えばテスト結果はこちら

/home/travis/build/rails/rails/activerecord/test/models/developer.rb:40: warning: mismatched indentations at 'end' with 'class' at 9
/home/travis/build/rails/rails/activesupport/lib/active_support/dependencies.rb:297:in `require': /home/travis/build/rails/rails/activerecord/test/models/developer.rb:36: syntax error, unexpected keyword_do, expecting keyword_end (SyntaxError)
/home/travis/build/rails/rails/activerecord/test/models/developer.rb:76: syntax error, unexpected keyword_end, expecting end-of-input

突然SyntaxErrorが発生しました。
このエラーはRuby側で他のBugを修正したことによります。

RubyのこのBugを修正したことで、以下のようなSyntaxが通らなくなってしまいました。

# RubyのBugチケットで報告された再現ケース
def foo(pr, options, &blk)
  p pr.call
end

foo -> { :hello }, a: 1 do end

# Railsの壊れたケース
has_and_belongs_to_many :projects_extended_by_name_and_block,
    -> { extending(DeveloperProjectsAssociationExtension) },
    :class_name => "Project",
    :join_table => "developers_projects",
    :association_foreign_key => "project_id" do
      def find_least_recent
        order("id ASC").first
      end
    end

RubyでFixが入りました

第1位 思わぬところからのPatch

スクリーンショット 2015-12-24 10.58.26.png

例えばテスト結果はこちら

  1) Error:
ActiveModel::Type::IntegerTest#test_casting_nan_and_infinity:
FloatDomainError: Infinity
    /home/travis/build/rails/rails/activemodel/lib/active_model/type/integer.rb:39:in `cast_value'
    /home/travis/build/rails/rails/activemodel/lib/active_model/type/value.rb:36:in `cast'
    /home/travis/build/rails/rails/activemodel/lib/active_model/type/helpers/numeric.rb:12:in `cast'
    /home/travis/build/rails/rails/activemodel/test/cases/type/integer_test.rb:35:in `block in <class:IntegerTest>'

Rubyのcaseの最適化で、Float::INFINITYのときも整数に変換していたため、このようなエラーが発生していました。

日本語で起票されたバグチケットにもかかわらず、"日本語はわからないけど、Rubyならわかる!"というコメントとともにFixされていったのが印象的でした。

スクリーンショット 2015-12-24 16.53.25.png

まとめ

trunkやmasterを眺めているのは、とてもエキサイティングなので、RubyやRailsを触っている皆様におかれましては、是非trunkやmasterを覗いてみてください。

40
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
35