2013-02-09 49 views
3

我有這個類:如何正確使用模擬?

class EnablePost 

    def initialize(post_klass, id) 
    raise "oops" if post_klass.blank? 
    @post_klass = post_klass 
    @id = id 
    end 

    def perform 
    post = @post_klass.find_by_id(@id) 
    return unless post 
    post.update_attribute :enabled, true 
    end 

end 

我必須寫規範檢驗以上:

describe EnablePost do 
    it "should enable a post" do 
    post = mock 
    post.should_receive(:blank?).and_return(false) 
    post.should_receive(:find_by_id).with(22).and_return(post) 
    post.should_receive(:update_attribute).with(:enabled, true) 
    result = EnablePost.new(Post, 22).perform 
    result.should be_true 
    end 
end 

但我真正想要做的是把EnablePost作爲一個黑盒子。我不想模擬:blank?:find_by_id:update_attribute。 也就是說我想我的規格看起來像:

describe EnablePost do 
    it "should enable a post" do 
    post = mock 
    result = EnablePost.new(post, 22).perform 
    result.should be_true 
    end 
end 

缺少什麼我在這裏?我使用錯誤嗎?

回答

2

是的,你在混淆模擬和存根。

搞好模擬解釋:http://jamesmead.org/talks/2007-07-09-introduction-to-mock-objects-in-ruby-at-lrug/

嘲笑:

  • 不同的事情,不同的人
  • 歧義的術語
  • 混淆使用Rails「嘲笑」

Mock對象:

  • 預期的方法調用預先設置
  • 驗證實際調用匹配,期望那些

還檢查了http://martinfowler.com/articles/mocksArentStubs.html [感謝用戶殭屍在評論]

如果您使用RSpec的,它將別名加倍,模擬和存根。 RSpec希望您選擇任何方法名稱都可以使您的代碼更加清晰。

您的第一部分測試代碼正確使用了「模擬」一詞。您正在設置預期將被調用的方法調用,然後執行它們。但是,您正在測試代碼的兩個不同區域:第一個區域是初始化方法,第二個區域是#perform方法。

您可能會發現更容易嘲弄和存根如果你寫的小方法:

# What you want to test here is the raise and the member variables. 
# You will stub the post_klass. 
def initialize(post_klass, post_id) # post_id is a better name 
    raise "oops" if post_klass.blank? 
    @post_klass = post_klass 
    @post_id = post_id # because we don't want to mask Object#id 
end 

attr_accessor :post_id 
attr_accessor :post_klass 

# What you want to test here is the post_klass calls #find_by_id with post_id. 
# See we've changed from using instance variables to methods. 
def post 
    post_klass.find_by_id(post_id) 
end 

# What you want to test here is if the update happens. 
# To test this, stub the #post method. 
def perform 
    p = post 
    return unless p 
    p.update_attribute :enabled, true 
end 

當你寫你這樣的代碼,你可以很容易地存根#post方法。

爲RSpec的源代碼示例見本示出的模擬和存根之間的差:

http://blog.firsthand.ca/2011/12/example-using-rspec-double-mock-and.html

+0

還檢查了http://martinfowler.com/articles/mocksArentStubs.html – Zombies 2013-02-09 09:18:52

相關問題