teacher_spec の記述
昨日の shared_example を使った Teacher モデルのテストを作成する.
-
spec/models がなければ作る
mkdir -p spec/models
-
spec/models/teacher_spec.rb を以下のように記載する. 実際には行わないテストも記載してあるが,既存のテンプレートから手動でコピーしたからである. 後日,このテンプレートの設定方法については説明する. 元々のソースにはコメントは書いてないが,説明のためにソースファイル内に解説をコメントとして掲示しておく.
require 'rails_helper' # Teacher model の仕様を開始 RSpec.describe Teacher, type: :model do # validation の共通テスト (使わないものはコメントしてある) context 'common validation check' do # テスト対象を指定する.今回は :hkob というキーで指定された FactoryBot である subject { teacher_factory :hkob } # ここから下に出てくる it_behaves_like は shared_examples.rb に記載した共有テストである. # 最初の引数が共有テストの名前,その後ろがそのテストに渡す引数である. # name と email 属性が nil の場合に validate に失敗することを確認(presence validation) # FactoryBot の作成確認のため,全ての属性が正しい場合に有効であることも確認している it_behaves_like :presence_validates, %i[name email] # email が重複している場合に validate に失敗することを確認 (unique validation) it_behaves_like :unique_validates, %i[email], -> { teacher_factory :taro } #it_behaves_like :plural_unique_validates, %i[], -> { teacher_factory :other } # モデルオブジェクトが削除できるかを確認 (destroy validation) it_behaves_like :destroy_validates #it_behaves_like :reject_destroy_validates #it_behaves_like :belongs_to, :teacher, has_many: %i[], has_one: %i[], children: :optional, child: :optional #it_behaves_like :dependent_destroy, :teacher, %i[has_many has_one] #it_behaves_like :reject_destroy_for_relations, :teacher, %i[has_many has_one] #it_behaves_like :destroy_nullify_for_relations, :teacher, %i[has_many has_one] end # 複数の FactoryBot を生成した後に実行できるメソッドの仕様 context 'after some teachers are registrered' do # 準備する FactoryBot の key を配列で指定しておく model_keys = %i[hkob taro] # 上記で作成した key 配列に対応した FactoryBot を targets として事前準備する. # let! なので,it の前に必ず準備された状態になる(遅延実行ではない). let!(:targets) { teacher_factories model_keys } # クラスメソッドに対する仕様 (今回はなし) describe 'Teacher class' do # テスト対象を Teacher クラスとする. subject { Teacher } # 今回はテストはない #it_behaves_like :mst_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], #end # #it_behaves_like :msta_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], # } #end # #it_behaves_like :mst, :METHOD1, -> { [v1, teacher_factories(model_keys.values_at()), v2, teacher_factories(model_keys.values_at())] } #it_behaves_like :msta, :METHOD1, -> { [v1, teacher_factories(model_keys.values_at()), v2, teacher_factories(model_keys.values_at())] } end # クラスインスタンスに対する仕様 context 'Teacher instances' do # 上記で作成した複数の FactoryBot をテスト対象とする. subject { targets } # amst は array method send test の略で配列の個々の要素に method を呼び出します. # ハッシュの中身は,キーが method 名, 値の配列が引数とその結果を並べたものです. # 引数がない場合には,引数の部分は nil とします. # 最初のテストでは targets のそれぞれに対して,name を渡した時に,その属性値である name の値が返ってくることを期待します. # ただし,この example 内で it は内部に一つだけしか存在しないため,block の最初だけしか targets が作られない. # インスタンスの値を変えるような副作用があるメソッドを読んだ場合には,後続のメソッドは副作用の影響を受けることに注意が必要である. # 逆に各メソッドのテストの度にインスタンスが作られないので,ベタに it を並べるよりもテストは高速化される. it_behaves_like :amst_block, -> t do { name: [nil, %w[小林弘幸 高専太郎]], email: [nil, %w[hkob@example.com taro@example.com]], } 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 による自動テストが駆動する.devise の設定でほとんどのテストは通過するが,name は後から追加した属性であるためテストに失敗している.
16:00:22 - INFO - Running: spec/models/teacher_spec.rb Running via Spring preloader in process 50685 Teacher common validation check behaves like presence_validates should be valid when name is nil should not be valid (FAILED - 1) when email is nil should not be valid behaves like unique_validates when another object has same email should not be valid behaves like destroy_validates should destroy should change `Teacher.count` by -1 after some teachers are registrered Teacher instances behaves like amst_block amst: name amst: email should receive above methods(amst) Failures: 1) Teacher common validation check behaves like presence_validates when name is nil should not be valid Failure/Error: is_expected.to_not be_valid expected #<Teacher id: 114, name: nil, email: "hkob@example.com", created_at: "2018-11-14 07:00:24", updated_at: "2018-11-14 07:00:24"> not to be valid Shared Example Group: :presence_validates called from ./spec/models/teacher_spec.rb:8 # ./spec/support/shared_examples.rb:10:in `block (4 levels) in <main>' # -e:1:in `<main>' Finished in 0.26226 seconds (files took 0.8522 seconds to load) 6 examples, 1 failure Failed examples:
-
name が nil の場合に validation が失敗する必要がある.app/models/teacher.rb に validates を追加する.
class Teacher < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :name, presence: {message: 'は空にはできません.'} end
- 保存をすると,また guard が起動し,全てのテストが通過する.
長くなったので今日はここまで