2010-03-10 57 views
3

我有問題得到rspec的運行正常,以測試validates_inclusion_of我的移民問題是這樣的:與validates_inclusion_of,acts_as_tree和RSpec

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 < ActiveRecord::Base 
    acts_as_tree 

    validates_presence_of :name 
    validates_uniqueness_of :name 
    validates_inclusion_of :parent_id, :in => 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 

我得到以下錯誤:

'Category should save with valid parent' FAILED Expected #<Category id: nil, name: "Category Two", parent_id: 5, created_at: nil, updated_at: nil> to be valid, but it was not Errors:

Parent is missing

在控制檯上的一切似乎是罰款,按預期方式工作:

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 

我運行軌道2.3.5,RSpec的1.3。 0和rspec-rails 1.3.2

任何人,任何想法?

回答

5

問題是,你不能撥打電話Category.all.map(&:id)validates_inclusion_of

的第一個跡象,這是當你嘗試運行的情況下將是顯而易見

rake db:migrate:down VERSION=<n> 
rake db:migrate:up VERSOIN=<n> 

其中<n>是創建分類模型遷移的版本號。

你會得到這樣的:

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打開控制檯。您將看到一個表格的SQL查詢:

SELECT * FROM "categories" 

在輸出中。這對應於Category.all.map(:&id)的呼叫。然而,一旦你開始輸入的命令,如:

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

,你會看到,查詢SELECT * from "categories"不會在日誌中出現。故事的寓意是只有常數纔會出現在調用validations_inclusion_of時,因爲那裏的代碼只會被評估一次。

控制檯代碼工作的唯一原因是因爲在先前的控制檯會話,你已經創建了id=1

一個Category對象,你可以寫一個自定義的驗證,你想要做什麼:

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測試就會通過。

+0

謝謝您的全面解釋,現在一切正常。 – jigfox 2010-03-15 10:11:46

0

實際上,只需將Category.all.map(&:id)放入proc/lambda中即可推遲枚舉計算。 寫作

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

將在驗證的時間內獲取類的ID,而不是在類聲明的時間。