2

我有一个简单的方法,我想用 RSpec 进行测试。我想确保applyplayer.capacity一。为此,我模拟了一个播放器对象并正在测试它是否接收到正确的消息。

代码

class DecreaseCapacity < Item
  def apply player
    player.capacity -= 1
  end
end

测试

describe DecreaseCapacity, "#apply" do
  it "should decrease capacity by one" do
    player = double()
    player.should_receive(:capacity)   # reads the capacity
    player.should_receive(:capacity=)  # decrement by one
    subject.apply player
  end
end

失败信息

1) DecreaseCapacity#apply should decrease the player's capacity by one
   Failure/Error: subject.apply player
   undefined method `-' for nil:NilClass
   # ./item.rb:39:in `apply'
   # ./item_spec.rb:25

这里发生了什么?为什么要player.capacity -= 1尝试-调用nil

4

1 回答 1

10

问题是你给玩家存根的方式在capacity被调用时会返回 nil 。你需要像这样改变:

player.should_receive(:capacity).and_return(0)
player.should_receive(:capacity=).with(1)

要了解原因,让我们分解代码中发生的情况。如果我们将其扩展-=为:

player.capacity = player.capacity - 1

使用您的存根播放器,它变成了这样:

player.capacity = nil - 1

这正是 RSpec 所抱怨的。

现在,让我建议一种更好的方法来编写测试。您的测试只是反映了您的实现,它不测试方法。我的意思是它不测试该apply方法是否将玩家的容量增加一——它测试apply调用capacity然后capacity=。您可能认为这是同一件事,但这只是因为您知道您是如何实现该方法的。

这就是我编写测试的方式:

it "increments a player's capacity" do
  player = Player.new # notice that I use a real Player
  player.capacity = 0
  subject.apply(player)
  player.capacity.should == 1
end

我使用了一个真实的Player对象而不是设置一个存根,因为我假设它的实现Player#capacity只是一个访问器,那里没有任何逻辑可以干扰我的测试。使用存根的风险在于,有时存根变得比真实对象更复杂(在这种情况下,我会争辩),这意味着您的测试比实际代码更有可能出错。

如果您想使用 RSpec 的完整表达能力,您也可以这样编写测试:

it "increments a player's capacity" do
  player = Player.new
  player.capacity = 0
  expect { subject.apply(player) }.to change { player.capacity }.to(1)
end
于 2010-12-22T06:58:34.563 回答