2012-07-04 37 views
17

我對Rails 3中的測試還很新,我使用RSpec和Remarkable。我已經閱讀了大量的文章和一些書籍,但當我們使用該協會的名稱和ID時,我仍然處於不確定的狀態。驗證和測試Rails 3關聯(使用RSpec/Remarkable)的完美方法?

class Project < ActiveRecord::Base 
    has_many :tasks 
end 

class Task < ActiveRecord::Base 
    belongs_to :project 
end 

由於沒有很好的做法,我要保護我的屬性,從質量分配:

class Task < ActiveRecord::Base 
    attr_accessible :project # Or is it :project_id?? 

    belongs_to :project 
end 

首先,我要確保項目從未存在沒有有效的任務:

class Task < ActiveRecord::Base 
    validates :project, :presence => true  # Which one is the... 
    validates :project_id, :presence => true # ...right way to go?? 
end 

我也想確保指定的項目或項目ID始終是有效的:

class Task < ActiveRecord::Base 
    validates :project, :associated => true  # Again, which one is... 
    validates :project_id, :associated => true # ...the right way to go? 
end 

...我需要驗證:我在使用時的存在:相關?

非常感謝澄清,似乎閱讀時間和嘗試使用的RSpec /早該來測試的東西后/顯着的我沒有看到,因爲所有的樹木了的森林......

+0

很好的清晰的問題。爲了確認,你是不是想要「確保任務**從不**沒有有效的(父項目)項目」? –

回答

12

這似乎是做了正確的方式:

attr_accessible :project_id 

你不必把:project那裏呢!這是反正可以做到task.project=(Project.first!)

然後,檢查:project_id的使用存在以下(當使用task.project=(...):project_id也被設置):

validates :project_id, :presence => true 

現在確保比相關的項目是有效的這樣:

validates :project, :associated => true 

所以:

t = Task.new 
t.project_id = 1 # Value is accepted, regardless whether there is a Project with ID 1 
t.project = Project.first # Any existing valid project is accepted 
t.project = Project.new(:name => 'valid value') # A new valid project is accepted 
t.project = Project.new(:name => 'invalid value') # A new invalid (or an existing invalid) project is NOT accepted! 

有點遺憾的是,當通過t.project_id =分配ID時,並沒有檢查這個特定的ID是否真的存在。您必須使用自定義驗證或使用Validates Existence GEM進行檢查。

爲了測試使用RSpec的具有顯着的匹配這些關聯,這樣做:

describe Task do 
    it { should validate_presence_of :project_id } 
    it { should validate_associated :project } 
end 
+2

哇這讓人大開眼界。我在Rails 3.2.3中獲得了意想不到的結果。嘗試這個。在任務模型中,添加'validates:project,:presence => true',但不添加':associated'驗證。在控制檯中,使用project_id = 999創建一個新任務並檢查它是否爲'.valid?'。 Rails在項目表中查找記錄,然後(可能是因爲找不到,project_id仍然爲零),驗證失敗。現在將驗證更改爲'validates:project_id,:presence => true'。 Rails不會讀取項目並驗證成功。添加':associated'並驗證*仍然*成功。 –

4
validates :project, :associated => true 
validates :project_id, :presence => true 

如果要確保存在關聯,則需要 測試是否存在用於映射關聯的外鍵, 而不是關聯的對象本身。 http://guides.rubyonrails.org/active_record_validations_callbacks.html

attr_accessible :project_id 
+3

這是來自文檔的直接引用,但是在使用Rails 3.2.3進行測試時,它完全相反。如果驗證關聯存在(':project,:presence => true'),則在項目表中不存在project_id時,驗證失敗。但是,如果您驗證id(':project_id,:presence => true')的存在,則無論您將哪個值賦給project_id,'.valid?'都會返回'true'。添加'validates:project,:associated => true'沒有幫助 - 它仍然接受不存在的project_ids。相當困惑如何應該驗證協會! –

3

編輯:假設該組織是不可選的...

我可以讓它徹底驗證的唯一方法是這樣的:

validates_associated :project 
validates_presence_of :project_id, 
    :unless => Proc.new {|o| o.project.try(:new_record?)} 
validates_presence_of :project, :if => Proc.new {|o| o.project_id} 

第一行驗證相關項目是否有效,如果有一個。第二行堅持project_id存在,除非關聯的項目存在並且是新的(如果它是新記錄,則它還沒有ID)。如果存在ID,則第三行確保項目存在,即,如果關聯的項目已經被保存。

如果您將已保存的項目分配給project,ActiveRecord將爲該任務分配project_id。如果您將未保存的/新項目分配到project,則它將保留空白project_id。因此,我們要確保project_id存在,但只有在處理已保存的項目時纔會這樣;這在上面的第二行中完成。相反,如果您爲代表真實項目的project_id指定一個數字,ActiveRecord將使用相應的Project對象填寫project。但是,如果您分配的ID是虛假的,則會將project設置爲零。因此,上面第三行確保我們有一個project如果project_id被填入 - 如果你提供一個僞造ID,這將失敗。

見RSpec的例子來測試這些驗證:https://gist.github.com/kianw/5085085

2

約書亞Muheim的解決方案工作,但我討厭被不能夠簡單地鏈接了一個項目,一項任務的ID是這樣的:

t = Task.new 
t.project_id = 123 # Won't verify if it's valid or not. 

所以我想出了這個:

class Task < ActiveRecord:Base 

    belongs_to :project 

    validates :project_id, :presence => true 
    validate :project_exists 

    private 
    def project_exists 
     # Validation will pass if the project exists 
     valid = Project.exists?(self.project_id) 
     self.errors.add(:project, "doesn't exist.") unless valid 
    end 

end