多对多唯一性约束测试不起作用

时间:2012-01-08 21:40:12

标签: validation ruby-on-rails-3.1 many-to-many rspec2 unique

我在Rails应用程序中与连接表有多对多的关系。我在我的模型中使用has_many:through idiom。为了简单起见,我们可以调用我的第一堂课程,第二堂课程和连接表课程注册(包含字段student_id和course_id)。我想确保给定的学生最多与一个给定的课程相关联一次(即{student_id,course_id}元组在注册表中应该是唯一的)。

所以我有一个强制执行这种唯一性的迁移。

def change
  add_index :enrollments, [:student_id, :course_id], :unique => true
end

此外,我的模型类定义如下:

class Student < ActiveRecord::Base
  has_many :enrollments  
  has_many :courses, :through => :enrollment

end

class Course < ActiveRecord::Base
  has_many :enrollments
  has_many :students, :through => :enrollment

end

class Enrollment < ActiveRecord::Base
  belongs_to :student
  belongs_to :course

  validates :student,    :presence => true
  validates :course,     :presence => true
  validates :student_id, :uniqueness => {:scope => :course_id}

end

在rails控制台中,我可以执行以下操作:

student = Student.first
course = Course.first
student.courses << course
#... succeeds
student.courses << course
#... appropriately fails and raises an ActiveRecord::RecordInvalid exception

在我的RSpec测试中,我做了完全相同的事情,我对以下代码没有异常:

@student.courses << @course
expect { @student.courses << @course }.to raise_error(ActiveRecord::RecordInvalid)

所以我的测试失败并报告:

expected ActiveRecord::RecordInvalid but nothing was raised

这里发生了什么?我能做错什么?我该如何解决?

2 个答案:

答案 0 :(得分:1)

Rails使用模型级验证,如果要严格检查是否需要使用数据库级别 - 例如外键。但在这种情况下,您需要从数据库连接器中捕获异常。 这很奇怪,因为在我的代码(非常类似于你)中验证了唯一的加注异常。

答案 1 :(得分:0)

这里可能会发生一些事情:

  1. @courses在两次使用之间发生了变化。
  2. @student在使用之间发生了变化。
  3. 使用let,您可以保护这些值不受期望值的影响。

    let(:course) { Course.first }
    let(:student) { Student.first }
    subject{ student.courses << course << course }
    it { should raise_error(ActiveRecord::RecordInvalid) }
    

    或者,您的代码可能有问题:)