2013-02-09 59 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