2016-02-11 50 views
0

下面是傳遞!Rspec控制器測試 - 如何存根和/或測試自定義對象

控制器代碼:

class OrdersController 
    def create 
    ... 
    @order.save 
    end 
end 

規格代碼:

describe OrdersController do 
    it "should call save method" do 
    Order.any_instance.should_receive(:save) 
    post :create 
    end 
end 

但是,如果只有它是那麼容易......我有被執行後,一些自定義的作業對象保存,所以代碼實際上看起來像這樣:

控制器代碼:

class OrdersController 
    def create 
    ... 
    @order.save 
    RoadrunnerEmailAlert.new.async.perform(@order.id, true) 
    CalendarInvite.new.async.perform(@order.id) 
    RoadrunnerTwilioAlert.new.async.perform(@order.id) 
    end 
end 

我很想測試自定義對象正在接受與正確的參數方法的鏈,但不知道怎麼回事,總之創造規範的代碼是這樣的:

before do 
    class RoadrunnerEmailAlert 
    def async 
    end 
    end 
end 

但是,這是如此人爲的,它肯定是不正確的...建議表示讚賞!

+0

你能嘗試這樣的事嗎?這對其他人也有幫助。 'RoadrunnerEmailAlert.new.async.stubs(:perform).with('some_id',true)。返回('your_expected_result')' –

+0

這並不奏效,但可能是因爲這項工作的性質,我爲#得到了'未定義的方法'存根''但即使我要在工作內部調用objet,'OrderMailer.new(id,true).deliver'我會得到'錯誤數量的參數'這意味着它不是骯髒的 – james

+0

你在這個控制器測試中應該關注的是A )Order對象被保存,B)每個異步作業被觸發。我強烈建議不要測試操作中調用哪些方法,但操作的結果是什麼。也就是說,您的測試應該聲明訂單記錄在數據庫中,並且每個異步作業的結果都是正確的。許多異步作業框架都有用於此目的的測試設置。你使用的是什麼異步作業框架? Sidekiq? – Chris

回答

0

如果這有助於其他人...這是一個非常全面的答案。

語境&設計說明

  1. 異步框架抽油杆衝寶石 (http://brandonhilkert.com/blog/why-i-wrote-the-sucker-punch-gem/)。 當時,這是我在使用 延遲作業,Sidekick等後最簡單的方法
  2. 基本上它的工作原理是這樣的:在控制器引用一個作業,然後引用其他任何東西(在我的情況下,一些PORO)
  3. 如果我真的嚴格測試,我想測試A)Controller正確調用Job並傳遞正確的參數,B)Job調用適當的PORO並傳遞正確的參數。但是,我只是測試了Controller調用適當的PORO並傳遞正確的參數,即Jobs已經在工作。

控制器代碼

@order.save 
RoadrunnerEmailAlert.new.async.perform(@order.id, true) 
CalendarInvite.new.async.perform(@order.id) 
RoadrunnerTwilioAlert.new.async.perform(@order.id) 

工作代碼

# app/jobs/roadrunner_email_alert.rb 
class RoadrunnerEmailAlert 
    include SuckerPunch::Job 
    def perform(order_id, require_tos) 
    ActiveRecord::Base.connection_pool.with_connection do 
     OrderMailer.success_email(order_id, require_tos).deliver 
    end 
    end 
end 

# app/jobs/calendar_invite.rb 
class CalendarInvite 
    include SuckerPunch::Job 
    def perform(order_id) 
    ActiveRecord::Base.connection_pool.with_connection do 
     CreateCalendar.new(order_id).perform 
    end 
    end 
end 

# app/jobs/roadrunner_twilio_alert.rb 
class RoadrunnerTwilioAlert 
    include SuckerPunch::Job 
    def perform(order_id) 
    ActiveRecord::Base.connection_pool.with_connection do 
     CreateAlert.new(order_id).perform 
    end 
    end 
end 

測試代碼

真正的大的東西在這裏,我不知道爲什麼我一直F orgetting(但只在測試中)是class和class的實例。對於PORO,因爲我正在實例化對象,所以我需要測試2個不同的「層」(首先適當地實例化對象,其次實例化對象適當地執行)。

require 'sucker_punch/testing/inline' 

describe "Controller code" do 
    before do 
    OrderMailer.any_instance.stub(:success_email) 

    mock_calendar = CreateCalendar.new(1) 
    CreateCalendar.stub(:new).and_return(mock_calendar) 
    CreateCalendar.any_instance.stub(:perform) 

    mock_alert = CreateAlert.new(1) 
    CreateAlert.stub(:new).and_return(mock_alert) 
    CreateAlert.any_instance.stub(:perform) 
    end 

    it "should call appropriate async jobs" do 
    expect_any_instance_of(OrderMailer).to receive(:success_email).with(1, true) 

    expect(CreateCalendar).to receive(:new).with(1) 
    expect_any_instance_of(CreateCalendar).to receive(:perform) 

    expect(CreateAlert).to receive(:new).with(1) 
    expect_any_instance_of(CreateAlert).to receive(:perform) 

    post :create 
    end 
end