validates_inclusion_of,acts_as_tree和rspec的问题

我有问题让rspec正常运行以测试validates_inclusion_of我的迁移如下所示:

class CreateCategories < ActiveRecord::Migration def self.up create_table :categories do |t| t.string :name t.integer :parent_id t.timestamps end end def self.down drop_table :categories end end 

我的模型看起来像这样:

 class Category  Category.all.map(&:id), :unless => Proc.new { |c| c.parent_id.blank? } end 

我的工厂:

 Factory.define :category do |c| c.name "Category One" end Factory.define :category_2, :class => Category do |c| c.name "Category Two" end 

我的模型规范看起来像这样:

 require 'spec_helper' describe Category do before(:each) do @valid_attributes = { :name => "Category" } end it "should create a new instance given valid attributes" do Category.create!(@valid_attributes) end it "should have a name and it shouldn't be empty" do c = Category.new :name => nil c.should be_invalid c.name = "" c.should be_invalid end it "should not create a duplicate names" do Category.create!(@valid_attributes) Category.new(@valid_attributes).should be_invalid end it "should not save with invalid parent" do parent = Factory(:category) child = Category.new @valid_attributes child.parent_id = parent.id + 100 child.should be_invalid end it "should save with valid parent" do child = Factory.build(:category_2) child.parent = Factory(:category) # FIXME: make it pass, it works on cosole, but I don't know why the test is failing child.should be_valid end end 

我收到以下错误:

‘类别应该保存有效的父级’FAILED预期#有效,但不是错误:

父母失踪了

在控制台上,一切似乎都很好,并按预期工作:

 c1 = Category.new :name => "Parent Category" c1.valid? #=> true c1.save #=> true c1.id #=> 1 c2 = Category.new :name => "Child Category" c2.valid? #=> true c2.parent_id = 100 c2.valid? #=> false c2.parent_id = 1 c2.valid? #=> true 

我正在运行rails 2.3.5,rspec 1.3.0和rspec-rails 1.3.2

任何人,任何想法?

问题是你无法在被调用的validates_inclusion_of调用Category.all.map(&:id)

当您尝试运行时,这种情况的第一个迹象将是显而易见的

 rake db:migrate:down VERSION= rake db:migrate:up VERSOIN= 

其中是创建Category模型的迁移的版本号。

你会得到类似的东西:

 in /Users/sseefried/tmp/so) == CreateCategories: reverting =============================================== -- drop_table(:categories) -> 0.0032s == CreateCategories: reverted (0.0034s) ====================================== (in /Users/sseefried/tmp/so) rake aborted! SQLite3::SQLException: no such table: categories: SELECT * FROM "categories" (See full trace by running task with --trace) 

这是因为rake尝试在运行迁移之前加载app/models/category.rb 。 由于Category模型不存在,因此失败。

另一种查看问题的方法是执行tail -f log/development.log ,然后尝试使用script/console打开script/console 。 您将看到表单的SQL查询:

 SELECT * FROM "categories" 

在输出中。 这对应于对Category.all.map(:&id)的调用。 但是,一旦你开始输入如下命令:

 c1 = Category.new, :name => "Category 1" 

您将看到SELECT * from "categories"中的查询SELECT * from "categories"不会再次出现在日志中。 故事的寓意只是常量可以出现在对validations_inclusion_of调用中,因为那里的代码只会被评估一次。

控制台代码工作的唯一原因是,在之前的控制台会话中,您创建了一个id=1Category对象

您可以编写一个自定义validation来执行您想要的操作:

 validate :parent_exists protected def parent_exists ids = Category.all.map(&:id) if !parent_id.blank? && !ids.member?(parent_id) errors.add(:parent_id, "does not point to a valid parent record") end end 

添加此代码后,您的rspec测试将通过。

实际上,您可以通过简单地将Category.all.map(&:id)放入proc / lambda来推迟可枚举的计算。 写作

  validates_inclusion_of :parent_id, in: proc{ Category.all.map(&:id) }, unless: proc{ |c| c.parent_id.blank? } 

将在validation时获取类别ID,而不是在类声明时。