はじめに
Rails Tips の 2 回目です。今日から数回は model のテストを簡単に書くための shared_example の紹介です。私の Shared example の雛形 (lib/templates/rspec/model/model_spec.rb
)はこんな感じになっています。
require "rails_helper" # RSpec.describe <%= class_name %>, type: :model do # let(:<%= singular_table_name %>) { <%= plural_table_name %> :<%= singular_table_name %> } # let(:can_delete) { <%= plural_table_name %> :can_delete } # context "属性に関する共通テスト" do # subject { can_delete } # let(:another_object) { <%= plural_table_name %> :another_object } # it_behaves_like "存在制約", %i[] # it_behaves_like "一意制約", %i[] # it_behaves_like "複合一意制約", %i[] # it_behaves_like "削除可能制約" # it_behaves_like "削除不可制約" # it_behaves_like "関連確認", :<%= singular_table_name %>, has_many: %i[], has_one: %i[], children: :optional, child: :optional # it_behaves_like "親削除時に自分も削除", :<%= singular_table_name %>, %i[has_many has_one] # it_behaves_like "親は削除不可", :<%= singular_table_name %>, %i[has_many has_one] # it_behaves_like "親削除時にnullを設定", :<%= singular_table_name %>, %i[has_many has_one] # end
属性やリレーションなどのモデルに関するテストはほとんど共通になるので、全部 shared_example で記述しています。今日からはこれを一つずつ紹介していきます。今日は最初の存在制約になります。
存在制約
モデルの属性のうち、null を許可しないものをチェックするための shared_example です。例えば、blog モデルの title が null になっては困る時には、blog_spec.rb は以下のようなテストになります。また、boolean は少し実装が異なるので enable も not null の制約をつけることにしてみます。
require "rails_helper" RSpec.describe Blog, type: :model do let(:blog) { blobs :blog } let(:can_delete) { blogs :can_delete } context "属性に関する共通テスト" do subject { can_delete } it_behaves_like "存在制約", %i[title enable] end end
これを実現するための「存在制約」 shared example は以下のようになります。こちらは spec/supports/shared_example.rb
に記載されています。
shared_examples_for "存在制約" do |keys| it { is_expected.to be_valid } keys&.each do |key| it "#{key} の内容が nil のとき、エラーになること" do subject[key] = nil expect(subject).not_to be_valid end end end
fixtures の状態でモデルが valid であることは、一番最初に確認します。あとは指定されたキーを一つずつ個別に nil にしたときに valid でなくなることを確認しているだけです。keys を一つも指定しなかった場合には、fixtures が正しく作成できているかのテストになります。このため、全ての model のテストで必ずこの shared_example は実行します。
正しく動作させるための実装
属性が null にならないことを検証することになるので、以下のコードを実装に記述することでテストを追加させることができます。boolean 以外の属性については、presence: true
で検証ができます。ただし、boolean の属性の場合値が false の場合に presence が true になりません。そこで、boolean 属性の場合には、inclusion で true または false のどちらかの値を取ることという検証にする必要があります(こちらは忘れてよくハマります)。
class Blog validates :title, presence: true validates :enable, inclusion: {in: [true, false]} end
おわりに
2回目にしてかなり短いブログになりました。毎日更新なので小出しになるのは許してください。
P.S. 公開後に boolean の話を追記しました。