RuboCopのCop実装で複数のRubyバージョンへのテストの書き方

RuboCop 実装者向けの小ネタ。

例えば Ruby 2.2 以上のみをターゲットとする Cop を作る場合のプロダクションコードの方は、以下のように TargetRubyVersionextend して minimum_target_ruby_versionRuby のバージョンを指定する。

class UnneededRequireStatement < Cop
  extend TargetRubyVersion

  minimum_target_ruby_version 2.2

テストコードの方は、以下のようになる。結果だけ見ると context の引数で Ruby の実行バージョンを指定するという見たままのものだと思うけれど、ジェネレーションで生成したコードをベースのままにすると上手く行かないので、そのあたりを最後に記す。

describe RuboCop::Cop::Lint::UnneededRequireStatement, :config do
  subject(:cop) { described_class.new(config) }

  context 'target ruby version < 2.2', :ruby21 do
    ...
  end

  context 'target ruby version >= 2.2', :ruby22 do
    ...
  end

RuboCop 0.52.0 現在のrake new_cop でジェネレーションしたテストコードは以下のようになっている。

describe RuboCop::Cop::Lint::UnneededRequireStatement do
  subject(:cop) { described_class.new(config) }

  let(:config) { RuboCop::Config.new }

複数の Ruby バージョンに対しての振る舞いを確認するテストを書く時はlet(:config) { RuboCop::Config.new } を消して describe の引数に :config を付けると良い。

-describe RuboCop::Cop::Lint::UnneededRequireStatement do
+describe RuboCop::Cop::Lint::UnneededRequireStatement, :config do
   subject(:cop) { described_class.new(config) }

-  let(:config) { RuboCop::Config.new }

このあたりを忘れるとバージョンごとの期待した結果を得ることができなくて、そのあたりも今年の RubyKaigi のときに pocke さんに相談していたりした内容だったりした。

そのとき相談していたベースは以下の PR で、このエントリの元にもなっている。

github.com