2012-08-03 43 views
0

我試圖測試控制器,以確保只有被授權方可以使用RSpec的查看正確的子對象RSpec的檢查控制。我無法弄清楚我在做什麼錯了,因爲我得到這個錯誤:與Model.create

ActiveRecord::RecordInvalid: Validation failed: Company can't be blank 

我有一個計劃目標和公司目標。該商店可以有很多計劃(想想一個害蟲控制公司)。我想測試一下,如果已知場景,我可以檢索公司的計劃(假設只有一個)。

的計劃是這樣的:

class Plan < ActiveRecord::Base 
    before_save :default_values 

    # Validation 
    validates :amount, :presence => true 
    validates :company, :presence => true 

    # Plans belong to a particular company. 
    belongs_to :company, :autosave => true  

    scope :find_all_plans_for_company, lambda { 
    |company| where(:company_id => company.id) 
    } 
    # Other code ... 

end 

公司看起來是這樣的:

class Company < ActiveRecord::Base 
    validates :name, :presence => true 
    validates :phone1, :presence => true 

    validates_format_of :phone1, :phone2, 
         :with => /^[\(\)0-9\- \+\.]{10,20}$/, 
         :message => "Invalid phone number, must be 10 digits. e.g. - 415-555-1212", 
         :allow_blank => true, 
         :allow_nil => true 

    has_many :users 
    has_many :plans 

end 

..控制器看起來像這樣

def index 
    @plans = Plan.find_all_plans_for_company(current_user.company) 

    respond_to do |format| 
     format.html # index.html.erb 
     format.json { render json: @plans } 
    end 
    end 

..和我的RSpec的測試檢查像這樣(對不起,如果它充滿了噱頭,我只是用它來解決問題,並且無法讓它工作)。

describe PlansController do 

    def valid_attributes 
    { 
     :company_id => 1, 
     :amount => 1000 
    } 
    end 

    describe "GET index" do 
    it "should return the Plans for which this users company has" do 

     @company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212") 
     Company.stub(:find).with(@company.id).and_return(@company) 

     controller.stub_chain(:current_user, :company).and_return(@company) 

     plan = Plan.create! valid_attributes 

     get :index, {} 
     assigns(:plans).should eq([plan]) 
    end 

    # Other tests ... 
    end 

end 

的問題是,當我嘗試這個(或任何我試過瘋狂的其他變種)我得到這個錯誤:

ActiveRecord::RecordInvalid: Validation failed: Company can't be blank 

我不知道這是爲什麼發生如我認爲Company.stub電話會爲我處理這個問題。但顯然不是。

缺少什麼我在這裏和我究竟做錯了什麼?我怎樣才能通過這個測試?

+0

你能後的控制器代碼呢? – 2012-08-03 23:29:39

+0

@shioyama - 已添加控制器。 – 2012-08-04 01:27:06

+0

對不起,我現在意識到問題發生在'get:index,{}'之前,所以控制器代碼並不重要。 – 2012-08-04 02:24:54

回答

2

讓我們剝離此規範中的圖層,以確保事情有意義(並確保我理解正在發生的事情)。首先,你在測試什麼?

it "should return the Plans for which this users company has" do 

    ... 

    assigns(:plans).should eq([plan]) 

所以,你要檢查與公司當前用戶相關的計劃被分配到@plans。我們可以剔除或嘲笑其他一切。

綜觀控制器代碼,我們有:

def index 
    @plans = Plan.find_all_plans_for_company(current_user.company) 

做什麼,我們需要得到這個工作,而無需訪問數據庫並且不依賴於模型?

首先,我們想從current_user.company得到一個模擬company。這就是這兩條線在規範的代碼做:

@company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212") 
    controller.stub_chain(:current_user, :company).and_return(@company) 

這將導致current_user.company返回模擬模型@company。到現在爲止還挺好。

現在到類方法find_all_plans_for_company。這是我有點困惑。在您的規範中,您將find方法存根Company以將@company替換爲id = 1

不過說真的,是不是足以只是做這樣的事情在你的控制器代碼?:

@plans = current_user.company.plans 

如果你這樣做了,那麼在您的測試,你可以只嘲笑一個計劃,然後返回它作爲plans協會您的模擬公司:

@plan = mock_model(Plan) 
    @company = mock_model(Company, :plans => [ @plan ]) 
    controller.stub_chain(:current_user, :company).and_return(@company) 

然後分配應該工作,而你並不需要實際創建任何模型或訪問數據庫。你甚至不需要給你的模擬公司一個id或任何其他屬性,這與規範無關。

也許我錯過了這裏的東西,如果是這樣,請讓我知道。

0

我有三個想法來了,可以解決你的問題:

  1. 嘗試增加attr_accessible :company_id到計劃類。

  2. 因爲當你創建的1 COMPANY_ID計劃驗證失敗,因爲它不存在於數據庫mock_model實際上並沒有保存到數據庫中。

  3. 確保before_save :default_values在計劃類不亂用新創建的實例的屬性的company_id。

+0

#1和#3沒有任何效果。但是我改變了這一行:@ company = mock_model(Company,:id => 1,:name =>「Test Company」,:phone1 =>「555-121-1212」)to this:@ company = Company.create :name =>「測試健身房」,:phone1 =>「555-555-5555」),它的工作原理。爲什麼我不能在這裏使用模擬? – 2012-08-04 03:28:03

+0

簡單來說,你正在創建一個真正的「Plan」並給它一個1的company_id,但是你從來沒有在DB中創建一個id爲1的真正公司,你只是在內存中創建了一個模擬模型。所以驗證公司的存在失敗了。該公司不存在。 – radixhound 2012-08-04 05:52:21

1

爲什麼你需要模擬?

我的標準測試設置是使用數據庫清理器,它從測試過程中創建的任何記錄中清除數據庫。通過這種方式,測試是在真實的數據庫記錄下運行的,這些記錄在每次測試後都會從測試數據庫中刪除。

您可能還想看看Factory Girl在測試過程中創建模型實例(例如,可以輕鬆創建10個公司記錄)。

參見:

+0

我也使用RSpec的FactoryGirl。這使得編寫測試變得更簡單,這也是一個需要考慮的因素。我確定自己的測試會自行清理(這對於RSpec來說很容易做到,因爲在測試過程中插入的所有行在測試之後都會回滾) – davidrac 2012-08-04 06:48:27

+0

這裏有一個折衷:在數據庫中使用實際記錄意味着您將控制器規格與模型代碼緊密耦合,並且由於您正在訪問數據庫而需要更多時間來執行。一般來說,如果規範不是太複雜,最好將模擬/存根調用模型類/實例。 – 2012-08-05 00:17:21

+0

但是,如果您完全從控制器測試中分離模型,那麼除了模型測試和控制器測試之外,還需要第三組測試來確保它們一起工作。由於系統最終作爲一個整體工作,這種方法的唯一優點是測試的速度? – 2012-08-05 23:48:46

相關問題