モデル同士の関連のテストは,ほぼ似たようなコードになります.これは手抜きをする価値があるということで,spec_helper.rb に以下のコードを書いています.実際には,昨日の Spork.prefork の中に書いてあります.
Spork.prefork do (中略) class SpecCreateDeleteCheck def initialize(check_class, hash) @check_class = check_class @check_keys = hash[:check_keys] || [] @relation_strings = hash[:relation_strings] || [] @one_relation_strings = hash[:one_relation_strings] || [] @attributes = hash[:attributes] || {} @method_name = ncm(hash[:method_name], '') { |o| o.to_s } @one_method_name = ncm(hash[:one_method_name], '') { |o| o.to_s } end attr_reader :check_class, :check_keys, :relation_strings, :one_relation_strings attr_reader :attributes, :method_name, :one_method_name def make_object_with_relations(relations) an_object = @check_class.create @attributes @relation_strings.each do |ww| ww.to_s.constantize.instance_method(@method_name).bind(relations[ww]).call << an_object end @one_relation_strings.each do |ww| ww.to_s.constantize.instance_method(@one_method_name + '=').bind(relations[ww]).call(an_object) end an_object end end def create_delete_check(check_class, hash) s = SpecCreateDeleteCheck.new(check_class, hash) create_check s delete_check s end def create_check(s) context "作成するとき" do before(:each) do @valid_attributes = s.attributes.dup end it "正しいアトリビュートに対して作成が成功すること" do s.check_class.create!(@valid_attributes) end s.check_keys.each do |key| it "#{key} 属性が設定されていない場合にバリデーションに失敗すること" do @valid_attributes.delete key s.check_class.new(@valid_attributes).should_not be_valid end end s.relation_strings.each do |w| it "#{w} に所属するオブジェクトが一つ増えること" do r = @relations[w] method = w.to_s.constantize.instance_method(s.method_name).bind(r) lambda { s.make_object_with_relations(@relations) }.should change(method.call, :count).by(1) end end s.one_relation_strings.each do |w| it "#{w} から #{s.one_method_name} として参照できること" do r = @relations[w] method = w.to_s.constantize.instance_method(s.one_method_name).bind(r) child = s.make_object_with_relations(@relations) method.call.should == child end end end end def delete_check(s) if s.relation_strings.length > 0 || s.one_relation_strings.length > 0 context "削除するとき" do before(:each) do @an_object = s.make_object_with_relations(@relations) end s.relation_strings.each do |w| it "#{ w } に所属するオブジェクトが一つ減ること" do method = w.to_s.constantize.instance_method(s.method_name).bind(@relations[w]) lambda { @an_object.destroy @an_object.should_not have(1).errors }.should change(method.call, :count).by(-1) end end s.one_relation_strings.each do |w| it "#{w}.#{s.one_method_name}が nil になること" do method = w.to_s.constantize.instance_method(s.one_method_name).bind(@relations[w]) end end end end end (以下省略) end
モデルの spec では,以下のようなコードを書きます.ここでは Syllabus というテーブルを追加した場合を例として記述します.Syllabus は Year と Ujnumber に所属しています(belongs_to).ただし,Year とは 1 対多 (has_many) の関係であり,Ujnumber とは 1 対 1 (has_one) の関係です.
# vim:set fileencoding=utf-8 filetype=ruby: require 'spec_helper' describe Syllabus do fixtures :syllabuses, :ujnumbers, :years before :each do @relations = { Ujnumber:ujnumbers(:ujnumber_digital_circuit_II), Year:years(:year_2009) } end create_delete_check Syllabus, attributes:{ gaiyou:"value for gaiyou", hyouka:"value for hyouka", kankei:"value for kankei", kyoukasho:"value for kyoukasho", susumekata:"value for susumekata", mokuhyou:"value for mokuhyou" }, check_keys:[ :gaiyou, :hyouka, :kankei, :susumekata, :mokuhyou ], relation_strings:[ :Year ], method_name: :syllabuses, one_relation_strings:[ :Ujnumber ], one_method_name: :syllabus end
create_delete_check の第一引数はテストをするクラス,二つの引数はチェック用のハッシュになっています.
- attributes
- 設定する属性値
- check_keys
- null チェックをする属性名
- relation_strings
- has_many チェックするクラス名
- method_name
- has_many のメソッド名
- one_relation_strings
- has_one チェックするクラス名
- one_method_name
- has_one のメソッド名
ハッシュなので has_many や has_one がない場合,null チェックしなくてもいい場合は,省略できます.
上記のテストを --format documentation で出力すると以下のようになります.数行書くだけでこれだけのテストを自動実行してくれるので,テーブルを新規に作成することが苦でなくなりました.
Syllabus 作成するとき 正しいアトリビュートに対して作成が成功すること gaiyou 属性が設定されていない場合にバリデーションに失敗すること hyouka 属性が設定されていない場合にバリデーションに失敗すること kankei 属性が設定されていない場合にバリデーションに失敗すること susumekata 属性が設定されていない場合にバリデーションに失敗すること mokuhyou 属性が設定されていない場合にバリデーションに失敗すること Year に所属するオブジェクトが一つ増えること Ujnumber から syllabus として参照できること 削除するとき Year に所属するオブジェクトが一つ減ること Ujnumber.syllabusが nil になること
本当は,親のオブジェクトを削除したときの挙動も入れようかと思ったのですが,dependent によっても異なりますし,元々削除することを想定していないテーブルなどもあるため,こちらは個別に実装しています.
あまり参考にはならないかと思いますが,とりあえず紹介まで.テスト自体もプログラムで書けるということで,いろいろな手抜きができますね.