我有一个简单的方法,我想用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
?
答案 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