2

在阅读 The RSpec Book 时,我遇到了as_null_object方法。我不明白这本书为什么需要它的解释:

...最简单的方法是告诉双重输出只听我们告诉它期望的消息并忽略任何其他消息。

但是为什么示例代码会失败?当我们double('output')在每个示例中调用时,我们不是为每个示例创建一个新的双对象并发送一条消息吗?

我想要的是对示例代码失败的原因以及如何as_null_object解决问题的更深入的解释(比书本)。

  it "sends a welcome message" do
    output = double('output')
    game = Game.new(output)
    output.should_receive(:puts).with('Welcome to Codebreaker!')
    game.start
  end

  it "prompts for the first guess" do
    output = double('output')
    game = Game.new(output)
    output.should_receive(:puts).with('Enter guess:')
    game.start
  end

这本书试图在前面的部分解释错误的原因,但我还是不明白这个解释。

我们在第一个示例中告诉替身期待puts“欢迎来到 Codebreaker!” 我们已经满足了这个要求,但我们只告诉它期待“欢迎使用 Codebreaker!” 它对“输入猜测:”一无所知

同样,第二个示例中的 double 期望“输入猜测:”,但它收到的第一条消息是“欢迎使用 Codebreaker”。

4

2 回答 2

2

当您创建一个 doubleoutput = double('output')并将其传递给 中的新游戏时Game.new(output),该 double 将收到它在 codebreaker 游戏代码中传递的每条消息。您尚未包含它,但该start方法具有以下代码:

module Codebreaker
  class Game
    ...
      def start
        @output.puts 'Welcome to Codebreaker!'
        @output.puts 'Enter guess:'
      end
  end
end

在这里,请记住 doubleoutput已被分配给'方法@output中的实例变量,因此在每个规范中,它会通过两条消息调用,首先是“欢迎使用 Codebreaker!”,然后是“输入猜测:”。gameinitialize

如果没有as_null_object,则output当它收到与预期不同的任何内容时,双重将失败,即在第一个规范中,除了“欢迎使用 Codebreaker!”之外的任何内容 在第二个规范中,除了“输入猜测:”之外的任何内容。通过as_null_object在替身上使用,你告诉它坐下来等待,忽略它所期望的以外的任何东西。这样就避免了上面的问题。

希望有帮助。

于 2012-10-03T22:44:08.223 回答
1

我同意解释不是很清楚。这就是我的理解,也许结合shioyama的回答会让这个点击。

创建规范时,您是说输出将包含预期的短语,但并不是说它仅包含预期的短语。因此,假设您通过将所有期望放在一个示例中来解决错误,如下所示:

   it "gets expected output" do
     output = double('output')
     game = Game.new(output)
     output.should_receive(:puts).with('Welcome to Codebreaker!')
     output.should_receive(:puts).with('Enter guess:')
     game.start
   end

这会过去的。问题是,如果您稍后决定让游戏开始时说“嗨,乔!”,那么它就会失败。然后您将不得不返回并修复您的规范,而实际上它已经满足规范。您需要一种机制让输出对意外输入做出反应而没有任何行为。这样,当出现意外情况时,您可以获得预期的特定输出示例,而不会失败。这似乎是非常基本的编程和单元测试断言,但在 RSpec 中,他们以您和我似乎不清楚的方式实现它。短语“空对象”带有很多包袱,似乎并没有描述它的含义正在做,但它是空对象模式(http://en.wikipedia.org/wiki/Null_Object_pattern)的实现。

于 2014-05-16T15:24:12.103 回答