RSpec mock завершается неудачно с "неопределенным методом` - 'для nil: NilClass " - PullRequest
2 голосов
/ 22 декабря 2010

У меня есть простой метод, который я хочу проверить с помощью 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 Ответ

10 голосов
/ 22 декабря 2010

Проблема в том, что если вы нажмете на игрока, он вернет ноль при вызове capacity. Вам нужно изменить так:

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
...