はじめに
Rails Tips の 14 回目です。連日の model のテストを簡単に書くための shared_example の紹介の続きです。しばらくは私の Shared example の雛形 (lib/templates/rspec/model/model_spec.rb
)を一つずつ解説していく形です。
# describe "一つの <%= class_name %> オブジェクトについて" do # subject { <%= singular_table_name %> } # it_behaves_like "配列内に存在?", %w[ # described_class.foo\ bar # ] # it_behaves_like "配列内に存在しない?", %w[ # described_class.foo\ bar # ] # it_behaves_like "同じ?", %w[ # described_class.foo\ bar # ] # it_behaves_like "一致しない?", %w[ # described_class.foo\ bar # ] # end
配列内に存在?
今日は「配列内に存在?」「配列内に存在しない?」になります。これらは where 条件の境界値チェックに使っています。
今回のサンプルは以前にも出てきた年度 (year) です。pre_years(year)
は指定された year よりも以前のものを取り出す scope です。year の年は含みます。pre_years_without_year(year)
も同様ですが year の年は含みません。最後の year_value_is(v)
は年度が のものだけに一致します。これらの境界値チェックをまとめてできるように、文字列の配列で scope を指示します。文字列なのはこれらが it の外側であるために fixtures や subject などが利用できないためです。また、%w を使っているため、区切り以外のスペースは \ を使ってエスケープしています。
describe "一つの #{described_class} オブジェクトについて" do subject { years :y2019 } it_behaves_like "配列内に存在?", %w[ described_class.pre_years\ subject described_class.pre_years_without_year\ years(:y2020) described_class.year_value_is\ 2019 ] it_behaves_like "配列内に存在しない?", %w[ described_class.pre_years\ years(:y2018) described_class.pre_years_without_year\ subject described_class.year_value_is\ 2018 ]
最初の「配列内に存在?」の shared_example は以下のようになります。文字列を eval で展開して得られた配列に subject が含まれるかどうかをチェックします。
shared_examples_for "配列内に存在?" do |*args| it "上記の配列に含まれること" do args.flatten.each do |str| print "\t#{str}\n" if documentation_formatter? expect(subject).to be_in? eval(str) end end end
同じくの「配列内に存在しない?」の shared_example は以下のようになります。こちらも文字列を eval で展開して得られた配列に subject が含まれないことをチェックします。これら二つの shared_example によって境界値がチェックできるわけです。
shared_examples_for "配列内に存在しない?" do |*args| it "上記の配列に含まれないこと" do args.flatten.each do |str| print "\t#{str}\n" if documentation_formatter? expect(subject).not_to be_in? eval(str) end end end
正しく動作させるための実装
条件判定なのでこれらは where を使った scope になります。year 属性の値を使って判定をしているだけです。
scope :pre_years, ->(y) { where arel_table[:year].lteq y.year } scope :pre_years_without_year, ->(y) { where arel_table[:year].lt y.year } scope :year_value_is, ->(y) { where arel_table[:year].eq y }
おわりに
今回は where を使った条件判断のテストを簡潔に書くための shared_example を紹介しました。基本的には、where の scope を書いた場合にはこれらを使ってテストを書くようにしています。