16

我对在 Rails 3 中进行测试还是很陌生,我使用 RSpec 和 Remarkable。我已经阅读了很多帖子和一些书籍,但我仍然有点不确定何时使用该协会的名称,何时使用它的 ID。

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

...当我使用 :related 时是否需要对 :presence 进行验证?

非常感谢您的澄清,似乎经过数小时的阅读和尝试使用 RSpec/Shoulda/Remarkable 测试东西后,由于所有的树木,我再也看不到森林了……

4

4 回答 4

11

这似乎是正确的方法:

attr_accessible :project_id

你也不必放在:project那里!无论如何都有可能做到task.project=(Project.first!)

然后检查是否存在:project_id使用以下内容(在使用:project_id时也设置task.project=(...)):

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来检查这一点。

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

describe Task do
  it { should validate_presence_of :project_id }
  it { should validate_associated :project }
end
于 2012-07-04T12:49:39.300 回答
4
validates :project, :associated => true  
validates :project_id, :presence => true

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

attr_accessible :project_id
于 2012-07-04T10:48:17.880 回答
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_id如果您将保存的项目分配给 ,ActiveRecord 将分配给任务project。如果您将未保存/新项目分配给project,它将project_id留空。因此,我们希望确保它project_id存在,但仅在处理已保存的项目时;这是在上面的第二行中完成的。

反之,如果你给project_id代表一个真实项目的数字分配一个数字,ActiveRecord 将填充project相应的项目对象。但是,如果您分配的 ID 是伪造的,它将保留project为 nil。因此上面的第三行,确保我们有一个projectifproject_id被填写——如果你提供一个虚假的 ID,这将失败。

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

于 2013-03-04T20:01:19.777 回答
2

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
于 2013-06-13T14:18:43.467 回答