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

我在这里想念什么?我是否错误地使用了模拟?

4

1 回答 1

2

是的,你混淆了模拟和存根。

一个很好的模拟解释:http: //jamesmead.org/talks/2007-07-09-introduction-to-mock-objects-in-ruby-at-lrug/

模拟:

  • 不同的人给不同的东西
  • 模棱两可的术语
  • 与 Rails 的“模拟”混淆</li>

模拟对象:

  • 预先设置的预期方法调用
  • 验证实际调用与预期调用匹配

另请查看http://martinfowler.com/articles/mocksArentStubs.html [感谢评论中的用户 Zombies]

如果您使用的是 RSpec,它的别名是 double、mock 和 stub。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

于 2013-02-09T09:06:38.277 回答