Rspec - 如何为一系列方法编写规范

时间:2013-03-31 13:03:17

标签: ruby rspec

我正在学习rspec,我想知道为一个调用其他方法链的方法编写规范的最有效方法。例如:

class Example1
  def foo(dependency)
     dependency.bar("A")
     dependency.baz("B")
     dependency.bzz("C")
  end
end

理想情况下,我想写这样的规格:

it "should call bar" do
   ex = Example1.new
   dep = mock
   dep.should_receive(:bar).with("A")
   ex.foo(dep)
end
it "should call baz"
   ... 
it "should call bzz"
   ...

然而,当我这样做时,我(可以理解)得到像'意外的方法调用baz'之类的例外。

那么处理这个问题的最佳方法是什么?我想出了一些想法,但我不知道它们中是否有任何好处。

  1. 使模拟依赖项成为“as_null_object”,因此忽略了额外的调用。 (下方 - 如果我在那个对象上调用不需要的随机内容,我就不知道了)
  2. 在每个规范中记录两个未使用的依赖方法调用(Down side - 感觉非常干)
  3. 在'之前'中删除所有三个依赖调用(向下 - 在'之前'放入大量垃圾)

3 个答案:

答案 0 :(得分:1)

RSpec有一个名为stub_chain的功能:https://www.relishapp.com/rspec/rspec-mocks/v/2-0/docs/stubs/stub-a-chain-of-methods

如何在一个例子中测试它们呢?

it "should call bar"
   ex = Example1.new
   dep = mock
   dep.should_receive("bar").with("A")
   dep.should_receive("baz").with("B")
   dep.should_receive("bzz").with("C")
   ex.foo(dep)
end

我相信你可以使用RSpec来验证它们被调用的顺序,如果这很重要的话。

然而,这种方法通常表明代码的编写方式存在问题,例如:德米特违法法则。在您的示例中,foo应该是依赖项的类。

答案 1 :(得分:1)

听起来你已经解决了RSpec给你的选项。我会选择选项1并使用as_null_object。确实,您可能缺少对该对象的其他随机方法调用,但如果每个测试的要点只是断言正在调用某个特定方法,我会很好,特别是如果我有更高级别的集成测试覆盖这种方法。

如果你真的需要验证在dependency上没有调用其他方法,那么选项3可能有意义,但是当实现发生变化时,这些测试可能会很脆弱。

顺便说一句,为了使您的测试更简单一点,您可以使用subject来避免显式实例化Example1(假设您正在使用describe Example1块),例如:

subject.foo(dep)

(但是参见评论中的讨论 - 隐含的主题可以隐藏意图)。

答案 2 :(得分:1)

我会用这种方式测试这段代码:

describe "checking foo method" do

  before(:each) do
    @ex = Example1.new
    @test = ClassOfDependency.any_instance
    @test.as_null_object
  end

  after(:each) do
    @ex.foo(dependency)
  end

  it "should call bar method" do
    @test.should_receive(:bar).with("A")
  end

  it "should call baz method" do
    @test.should_receive(:baz).with("B")
  end

  it "should call bzz method" do
    @test.should_receive(:bzz).with("C")
  end
end

但我不确定它会起作用,希望它会给你一些想法。