0

我正在学习 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. 在“之前”中存根所有三个依赖调用(不利的一面 - 在“之前”中放置了很多垃圾)
4

3 回答 3

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应该是依赖项类的方法。

于 2013-03-31T14:26:56.370 回答
1

听起来您已经确定了 RSpec 为您提供的选项。我会选择选项 1 并使用as_null_object. 确实,您可能会错过对该对象的其他随机方法调用,但如果每个测试的目的只是断言正在调用特定方法,我会同意的,特别是如果我有更高级别的集成测试涵盖这种方法。

如果您确实需要验证没有调用其他方法,dependency则选项 3 可能有意义,但当实现更改时,此类测试可能会变得脆弱。

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

subject.foo(dep)

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

于 2013-03-31T14:30:12.143 回答
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

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

于 2013-03-31T14:56:21.463 回答