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

＜この記事は「[Money Forward Advent Calendar 2015](http://qiita.com/advent-calendar/2015/moneyforward)」の12日目の記事です＞

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

## 前書き

[RailsのCI](https://travis-ci.org/rails/rails)ではRailsのmasterとRubyのtrunkでテストが回っています。普段、趣味でこのテストを眺めていて、テストが壊れたりすると原因を調べてFixしたりしています。

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

## 第３位　突然の大量Error

<img width="999" alt="スクリーンショット 2015-12-24 10.57.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/32923/1f0c39f9-692a-ee1d-c77e-2f8efec98079.png">


例えばテスト結果は[こちら](https://travis-ci.org/rails/rails/jobs/90460509)。

```
  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を１引数の関数(引数をkeyとして、それに対応するvalueを返す関数)に変換するためのメソッドです。

```ruby
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側の変更でテストが盛大に壊れていました。

```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側を修正](https://github.com/rails/rails/pull/22257)して対応しました。

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

## 第２位　突然のSyntaxError

<img width="982" alt="スクリーンショット 2015-12-24 10.58.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/32923/01a7b934-b6f2-ebe2-49e0-9f31ff328543.png">

例えばテスト結果は[こちら](https://travis-ci.org/rails/rails/jobs/72011838)。

```
/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](https://bugs.ruby-lang.org/issues/11107)を修正したことで、以下のようなSyntaxが通らなくなってしまいました。

```ruby
# 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が入りました](https://github.com/ruby/ruby/commit/dfec9d978804fbd53df9e6b93e5801985b2b3130)。


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

<img width="1011" alt="スクリーンショット 2015-12-24 10.58.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/32923/cba6c104-2207-b8cf-65b4-3bd98de6f3c7.png">

例えばテスト結果は[こちら](https://travis-ci.org/rails/rails/jobs/95972030)。

```
  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`のときも整数に変換していたため、このようなエラーが発生していました。

日本語で起票された[バグチケット](https://bugs.ruby-lang.org/issues/11804)にもかかわらず、"日本語はわからないけど、Rubyならわかる！"というコメントとともにFixされていったのが印象的でした。

![スクリーンショット 2015-12-24 16.53.25.png](https://qiita-image-store.s3.amazonaws.com/0/32923/5847d5c0-c94a-d2ae-d745-ec5253816e28.png)

## まとめ

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