値限定制約 shared example: 小林研 Rails Tips (72)

このページの内容は以下のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。

https://github.com/hkob/hkob_blog

はじめに

Rails Tips の 72 回目です。昨日、status 属性を追加しましたが、この属性は public, private, archived だけの値だけを設定できるものになっています。これをテストできるような値限定制約という shared example を追加したいと思います。

Rails をはじめよう - Railsガイド

値限定制約 shared example

値限定制約は keyok_values, ng_values を渡し、ok_values の場合にはエラーにならず、ng_values の場合にはエラーになることを確認します。存在制約を真似て以下のように作ってみました。

shared_examples_for "値限定制約" do |key, ok_values, ng_values|
  ok_values.each do |value|
    it "#{key} の内容が #{value} のとき、エラーにならないこと" do
      subject[key] = value
      expect(subject).to be_valid
    end
  end

  ng_values.each do |value|
    it "#{key} の内容が #{value} のとき、エラーになること" do
      subject[key] = value
      expect(subject).not_to be_valid
    end
  end
end

article_spec と comment_spec は以下のようになります。

    it_behaves_like "存在制約", %i[title body]
    it_behaves_like "値限定制約", :status, %w[public private archived], %w[NG]
    it_behaves_like "存在制約", %i[commenter body article_id]
    it_behaves_like "値限定制約", :status, %w[public private archived], %w[NG]

migration しておきます。

class Article < ApplicationRecord
  VALID_STATUSES = %w[public private archived]

  validates :title, presence: true
  validates :body, presence: true, length: { minimum: 10 }
  validates :status, inclusion: { in: VALID_STATUSES }
  has_many :comments, dependent: :destroy
end
class Comment < ApplicationRecord
  VALID_STATUSES = %w[public private archived]

  validates :commenter, presence: true
  validates :body, presence: true, length: { minimum: 10 }
  validates :status, inclusion: { in: VALID_STATUSES }
  belongs_to :article
  scope :order_created_at_desc, -> { order(created_at: :desc) }
end

どちらのテストも以下のように成功しました。

    behaves like 値限定制約
      status の内容が public のとき、エラーにならないこと
      status の内容が private のとき、エラーにならないこと
      status の内容が archived のとき、エラーにならないこと
      status の内容が NG のとき、エラーになること

おわりに

値限定制約を追加しました。あまり使うことは多くないかもしれませんが、自分の shared example にも追加しておきます。