validation和测试Rails 3关联的完美方法(使用RSpec / Remarkable)?

我仍然很擅长在Rails 3中测试,我使用RSpec和Remarkable。 我已经阅读了很多post和一些书籍,但是在使用关联名称的时候,我仍然有点不确定。

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  true # Which one is the... validates :project_id, :presence => true # ...right way to go?? end 

我还想确保分配的项目或项目ID始终有效:

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

…我需要validation:使用时存在:关联?

非常感谢澄清,似乎经过几个小时的阅读并尝试使用RSpec / Shoulda / Remarkable测试东西我不再看到森林,因为所有的树木了…

这似乎是正确的方法:

 attr_accessible :project_id 

您也不必将:project放在那里! 无论如何可以做task.project=(Project.first!)

然后使用以下命令检查是否存在:project_id: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是否确实存在。 您必须使用自定义validation或使用validation存在GEM来检查这一点。

要使用带有Remarkable匹配器的RSpec测试这些关联,请执行以下操作:

 describe Task do it { should validate_presence_of :project_id } it { should validate_associated :project } end 
 validates :project, :associated => true validates :project_id, :presence => true 

如果要确保存在关联,则需要测试是否存在用于映射关联的外键,而不是关联对象本身。 http://guides.rubyonrails.org/active_record_validations_callbacks.html

 attr_accessible :project_id 

编辑:假设该关联不是可选的…

我可以让它彻底validation的唯一方法是:

 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} 

第一行validation关联的项目是否有效(如果有)。 第二行坚持存在project_id ,除非关联的项目存在且是新的(如果它是新记录,它还没有ID)。 如果存在ID,则第三行确保项目存在,即,如果已保存关联的项目。

如果将已保存的项目分配给项目,ActiveRecord将为该任务分配project_id 。 如果将未保存的/新项目分配给project ,则会将project_id留空。 因此,我们希望确保project_id存在,但仅在处理已保存的项目时; 这是在上面第二行完成的。

相反,如果为表示真实项目的project_id分配一个数字,ActiveRecord将使用相应的Project对象填充project 。 但是如果您分配的ID是伪造的,它将使project为零。 因此,上面的第三行,如果你填写了project_id ,它确保我们有一个project – 如果你提供了一个虚假的ID,这将失败。

请参阅测试这些validation的RSpec示例: https : //gist.github.com/kianw/5085085

Joshua 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