RSpec模拟失败,“未定义方法` - '为nil:NilClass”

时间:2010-12-22 02:25:54

标签: ruby unit-testing rspec rspec2

我有一个简单的方法,我想用RSpec测试。我想确保apply递减player.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

1 个答案:

答案 0 :(得分: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方法将玩家的容量增加1 - 它测试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