同じ?・一致しない? shared_example: 小林研 Rails Tips (15)

はじめに

Rails Tips の 15 回目です。連日の 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 条件の境界値チェックでしたが、こちらは一意のものを取り出すメソッドのテストになります。記述法は昨日と同じで文字列で指定します。%w を使っているため、区切り以外のスペースは \ を使ってエスケープしています。ここでは、曜日 (week) クラスにおいて wday の数値(0から6)から特定の曜日を取り出すメソッド week_from_wday をテストしています。

describe "一つの #{described_class} オブジェクトについて" do
    subject { weeks :monday }
    it_behaves_like "同じ?", %w[
      described_class.week_from_wday\ 1
    ]

    it_behaves_like "一致しない?", %w[
      described_class.week_from_wday\ 6
    ]
  end

最初の「同じ?」の 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 eq eval(str)
    end
  end
end

同じくの「一致しない?」の 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).not_to eq eval(str)
    end
  end
end

正しく動作させるための実装

Week は sort_order に月曜日から日曜日までに対応する 1 から 7 の値が入っています。与えられる数値が 1 から 7 であれば week_from_sort_order で検索できるのですが、wday は日曜日が 0 で示される数値であるため、その場合のみ 7 を検索しています。

# @param [Integer] so sort_order
  # @return [Week] 月曜日
  def self.week_from_sort_order(so)
    Week.find_by sort_order: so
  end

  # @param [Integer] wday 週を表す数値
  # @return [Week] wday に対応した週
  def self.week_from_wday(wday)
    week_from_sort_order(wday.zero? ? 7 : wday)
  end

おわりに

今回は一意のオブジェクト取り出すメソッドのテストを記述しました。これでモデルクラスに関するテストはほぼ終わりです。次回からは複数のモデルオブジェクトに関するテストを紹介します。