💡
この記事は Middleman 時代に書いた古いものです。記録のため、astro-notion-blog に移行していますが、あまり参考にしないでください。
kurasu_spec.rb の修正
昨日生成した kurasu_spec をクラスに合わせて設定する.
-
spec/factories/kurasu.rb に書かれている属性の初期値を全て消す.
FactoryBot.define do factory :kurasu do end end
-
spec/models/kurasu_spec.rb を編集する.最初に以下の文よりも上の部分を spec/support/kurasu_factory.rb に保存する.
##### ↑ write to spec/support/create_factories/kurasu_factory.rb
-
テストに耐えうるデータを spec/support/create_factories/kursu_factory.rb に作成する.
KurasuFactoryHash = { hkob2300: %w[hkob 2300 f], hkob5300o: %w[hkob 5300 t], taro3300: %w[taro 3300 f] } # @param [Symbol, String] key オブジェクトを一意に決定するキー # @return [Kurasu] Kurasu FactoryGirl オブジェクト def kurasu_factory(key) t, n, o = KurasuFactoryHash[key.to_sym] FactoryBot.find_or_create( :kurasu, teacher_id: teacher_factory(t.to_sym).id, name: n, obsolete: o == 't', ) if t end # @param [Array<Symbol, String>] keys オブジェクトを一意に決定するキーの配列 # @return [Array<Kurasu>] Kurasu FactoryGirl オブジェクトの配列 def kurasu_factories(keys) keys.map { |k| kurasu_factory(k) } end
-
spec/models/kurasu_spec.rb を記述する.前回と同様ソースにはコメントを書いていないが,説明のためにコメントを追加した.また,わかりきっている amst_block の name のようなテストは普段はやらないが,今回は説明のために記述している.個別の詳細はソース内のコメントを参照のこと.
require 'rails_helper' RSpec.describe Kurasu, type: :model do context 'common validation check' do subject { kurasu_factory :hkob2300 } # name, obsolete, teacher_id 属性が nil の場合に validate に失敗することを確認(presence validation) it_behaves_like :presence_validates, %i[name obsolete teacher_id] #it_behaves_like :unique_validates, %i[], -> { kurasu_factory :other } # name, teacher_id の組み合わせたが重複している場合に validate に失敗することを確認 (plural unique validation) it_behaves_like :plural_unique_validates, %i[name teacher_id], -> { kurasu_factory :taro3300 } # モデルオブジェクトが削除できるかを確認 (destroy validation) it_behaves_like :destroy_validates #it_behaves_like :reject_destroy_validates # teacher への belong_to が正しく動作するか,また削除した時に teacher.kurasus が一つ減るかを確認 it_behaves_like :belongs_to, :kurasu, has_many: %i[teacher] # teacher を削除した時に,連動して kurasu が削除されるかを確認 it_behaves_like :dependent_destroy, :kurasu, %i[teacher] #it_behaves_like :reject_destroy_for_relations, :kurasu, %i[has_many has_one] #it_behaves_like :destroy_nullify_for_relations, :kurasu, %i[has_many has_one] end context 'after some kurasus are registrered' do model_keys = %i[hkob2300 hkob5300o taro3300] let!(:targets) { kurasu_factories model_keys } describe 'Kurasu class' do subject { Kurasu } # order_name の scope の確認 it_behaves_like :mst_block, -> t do { # 名前の順でソートできるか order_name: [nil, t.values_at(0, 2, 1)], } end # not_obsolete, teacher_is の scope の確認 it_behaves_like :msta_block, -> t do { # 有効なものだけ取得できるか not_obsolete: [nil, t.values_at(0, 2)], # teacher が hkob のものだけ取得できるか teacher_is: [t.first.teacher, t.values_at(0, 1)], } end #it_behaves_like :mst, :METHOD1, -> { [v1, kurasu_factories(model_keys.values_at()), v2, kurasu_factories(model_keys.values_at())] } #it_behaves_like :msta, :METHOD1, -> { [v1, kurasu_factories(model_keys.values_at()), v2, kurasu_factories(model_keys.values_at())] } end context 'Kurasu instances' do subject { targets } # name の確認 it_behaves_like :amst_block, -> t do { name: [nil, %w[2300 5300 3300]], } end #it_behaves_like :amsta_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], # } #end #it_behaves_like :amst, :METHOD1, -> { [v1, a1, v2, a2] } #it_behaves_like :amsta, :METHOD1, -> { [v1, a1, v2, a2] } end end end
-
この状況での guard の出力(仕様)はこんな感じになる.かなりの部分がエラーになっていることが確認できる(計8箇所).
Kurasu common validation check behaves like presence_validates should be valid when name is nil should not be valid (FAILED - 1) when obsolete is nil should not be valid (FAILED - 2) when teacher_id is nil should not be valid (FAILED - 3) behaves like plural_unique_validates when another object has same name, teacher_id should not be valid (FAILED - 4) when another object has at least one different keys(name, teacher_id) should be valid behaves like destroy_validates should destroy should change `Kurasu.count` by -1 behaves like belongs_to when destroying kurasu teacher.kurasus.count should decrease (FAILED - 5) behaves like dependent_destroy when destroying teacher should be destroyed by dependency (FAILED - 6) after some kurasus are registrered Kurasu class behaves like mst_block mst: order_name should receive above methods(mst) (FAILED - 7) behaves like msta_block should receive above methods(msta) (FAILED - 8) Kurasu instances behaves like amst_block amst: name should receive above methods(amst)
kurasu.rb の修正
kurasu_spec のテストが通過するように app/models/kurasu.rb に実装を記述する.
-
最初の 3 つのエラーは behaves like presence_validates のものである. name と teacher_id については,前回の teacher と同様に presence validation を追加するだけでよい.
validates :name, :teacher_id, presence: {message: 'は空にはできません'}
-
obsolete は boolean なので,presence validation では確認できない(false は presence で判定できないため). そのため,inclusion validation を使う.
validates :obsolete, inclusion: {in: [true, false], message: 'は空にはできません'}
-
plural unique validation については,uniqueness に scope をつけることで実現する.
validates :name, uniqueness: {scope: :teacher_id, message: '教員とクラスの組はユニークである必要があります.'}
-
belongs_to と dependent_desctroy の validation はそれぞれ関連を設定すれば満足する.まず,app/models/kurasu.rb の方に belongs_to を追加する.
# @return [Teacher] 関連する教員 belongs_to :teacher
-
app/models/teacher.rb の方には反対側の has_many を追加する.
# @return [Array<Kurasu>] 関連するクラス一覧 has_many :kurasus, dependent: :destroy
-
残るは三つの scope の設定である.app/models/kurasu.rb に scope を二つ登録する. いつもは scope に関して arel_table を使いまくっているが,今回は初心者用に通常の Rails の書き方で記述している. そもそも arel_table は Rails のプライベートメソッドなので,積極的に使うものではない.
scope :order_name, -> { order :name } scope :not_obsolete, -> { where obsolete: false } scope :teacher_is, -> t { where teacher_id: t.id }
-
これらを修正すると,テストは全て成功する.
Kurasu common validation check behaves like presence_validates should be valid when name is nil should not be valid when obsolete is nil should not be valid when teacher_id is nil should not be valid behaves like plural_unique_validates when another object has same name, teacher_id should not be valid when another object has at least one different keys(name, teacher_id) should be valid behaves like destroy_validates should destroy should change `Kurasu.count` by -1 behaves like belongs_to when destroying kurasu teacher.kurasus.count should decrease behaves like dependent_destroy when destroying teacher should be destroyed by dependency after some kurasus are registrered Kurasu class behaves like mst_block mst: order_name should receive above methods(mst) behaves like msta_block msta: not_obsolete msta: teacher_is should receive above methods(msta) Kurasu instances behaves like amst_block amst: name should receive above methods(amst) Finished in 0.31014 seconds (files took 0.39904 seconds to load) 12 examples, 0 failures
長くなったので今日はここまで