2012-01-16 63 views
1

當從構造方法中調用存根方法我已經得到了下面的模型使用製造

class User < ActiveRecord::Base 
    before_create :set_some_values 

    private 
    def set_some_values 
    #do something 
    end 
end 

在規範我使用Fabrication gem來創建對象,但我不能找到一種方法來存根set_some_values方法。我試過

User.any_instance.stub!(:set_some_values).and_return(nil) 

但製造似乎忽略了這一點。有可能嗎?

回答

0

當您在記錄中調用#save時,將調用#set_some_values方法。所以它無關的構造,因此,你不需要存根User.any_instance - 只要讓你的記錄,然後做一個局部的存根,如:

record.stub(:set_some_values) 
record.save 
+0

但是,我將不得不做'Fabricate.build',然後存根和保存,而不是隻是創建。 – RocketR 2012-05-15 18:03:55

+0

好的,那麼你想在這裏測試什麼?在這種情況下,測試與#set_some_values無關,所以您只需創建一個用戶並將#set_some_values的副作用存根刪除? – 2012-05-16 16:54:28

+0

是的。我並不在乎這個方法本身,只需要把它拿下來。 _P.S._好吧,現在我想我可以只存留'set_some_values'所使用的對象。 – RocketR 2012-05-16 20:04:13

2

這就是爲什麼我不像ActiveRecord回調 - 因爲如果你想與回調沒有關係(因爲你打電話給回調中的外部服務),你仍然需要關心它的存在。是的,你可以在回調中刪除方法,但它是同樣的問題,實際上它有點更糟,因爲現在你關心的是裏面的,這是一種你不想要的方法。

像往常一樣,這裏有多個選項。

過去我曾經使用過的一個選項是,在你的回調中添加一個條件,默認關閉它。因此,郵政類可能看起來像:

class Post 
    before_save :sync_with_store, :if => :syncing_with_store? 

    def syncing_with_store?; @syncing_with_store; end 
    attr_writer :syncing_with_store 

    def sync_with_store 
    # make an HTTP request or something 
    end 
end 

現在只要你真的想調用回調(也許在您的控制器或其它地方),你可以設置post.syncing_with_store = true你打電話之前post.save

這種方法的缺點是,你(和其他開發人員一起工作)必須牢記在心,而且你不得不這麼做。另一方面,如果你忘記這麼做,沒有什麼不好的事情發生。

另一種選擇是使用假類。假設你有一個Post,在保存時將其數據推送到外部數據存儲。您可以提取推送到單獨的類(例如Pusher)的代碼,該代碼可以在Post.pusher_service訪問。但是,默認情況下,這將被設置爲一個假的Pusher類,它響應相同的接口但什麼也不做。所以像:

class Post 
    class << self 
    attr_accessor :pusher_service 
    end 
    self.pusher_service = FakePostPusher 

    before_save :sync_with_store 

    def sync_with_store 
    self.class.pusher_service.run(self) 
    end 
end 

class FakePostPusher 
    def self.run(post) 
    new(post).run 
    end 

    def initialize(post) 
    @post = post 
    end 

    def run 
    # do nothing 
    end 
end 

class PostPusher < FakePostPusher 
    def run 
    # actually make the HTTP request or whatever 
    end 
end 

在您的生產環境文件中,您將設置Post.pusher_service = Pusher。在個別測試或測試案例中,您將創建Post - let(:klass) { Class.new(Post) }的子類 - 並設置klass.pusher_service = Pusher(這樣您不會永久設置它並影響將來的測試)。

我一直在嘗試的第三種方法是:簡單地不要使用ActiveRecord回調。這是我從Gary Bernhardt's screencasts(其中,順便說一句,相當驚人)拿起的東西。相反,定義一個包裝創建帖子行爲的服務類。喜歡的東西:

class PostCreator 
    def self.run(attrs={}) 
    new(attrs).run 
    end 

    def initialize(attrs={}) 
    @post = Post.new(attrs) 
    end 

    def run 
    if @post.save 
     make_http_request 
     return true 
    else 
     return false 
    end 
    end 

    def make_http_request 
    # ... 
    end 
end 

這樣PostCreator.run(attrs)是創建一個職位,而不是通過後會事實上的方式。現在要在Post中測試保存,不需要存儲回調。如果你想測試PostCreator過程,沒有魔法發生,你可以很容易地找出你想要的任何方法或獨立測試它們。 (你可以爭辯說,這裏提到的方法與截取AR回調相同,但是我認爲它更明確地發生了什麼。)顯然這隻能處理後期創建,但對於後期更新也可以做同樣的事情。

無論如何,不​​同的想法,挑選你的毒藥。

+1

感謝您的詳細介紹。加里伯恩哈特的屏幕錄像+1,他們確實很棒。 – RocketR 2012-05-17 21:02:03