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=1
的Category
对象
您可以编写一个自定义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,而不是在类声明时。