rspecで継承したクラスにany_instance.stubを使うとSystemStackError (stack level too deep) になる

タイトルのままですが、rspecで

User.any_instance.stub(:enabled?) { true }

のような処理をする際、SystemStackError stack level too deepが発生することがあります。

これは、継承の親クラスに対してany_instance指定し、実際には子クラスのメソッドが呼び出された場合に発生します。
仕様のようです。
https://github.com/rspec/rspec-mocks/issues/94

この辺りで無限ループしていますね。

# gems/rspec-mocks-2.10.1/lib/rspec/mocks/any_instance/recorder.rb:173
def observe!(method_name)
  stop_observing!(method_name) if already_observing?(method_name)
  @observed_methods << method_name
  backup_method!(method_name)
  @klass.class_eval(<<-EOM, __FILE__, __LINE__)
    def #{method_name}(*args, &blk)
      self.class.__recorder.playback!(self, :#{method_name}) # ここ
      self.__send__(:#{method_name}, *args, &blk)
    end
  EOM
end

特に、単一テーブル継承(UserのサブクラスMemberとAdminを作るなど)を使った場合に、うっかりやりがちです。
解決策としては、子クラス(MemberやAdmin)に直接any_instance指定すればOKです。

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

baba

ゆとりプログラマー。 高校時代から趣味でプログラミングを初め、そのままコードを書き続けて現在に至る。慶應義塾大学環境情報学部(SFC)卒業。BPS設立初期に在学中から参加している最古参メンバーの一人。得意分野はWeb全般、Ruby on Rails、Androidアプリケーションなど。最近はBlinkと格闘中。軽度の資格マニアで、情報処理技術者試験(高度10区分)などを保有。

babaの書いた記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ