Поскольку я начал использовать rspec, у меня была проблема с понятием светильников. Мои основные проблемы заключаются в следующем:Является ли плохой практикой случайное генерирование тестовых данных?
Я использую тестирование, чтобы выявить удивительное поведение. Я не всегда достаточно сообразителен, чтобы перечислять все возможные примеры кросс-тестов для примеров, которые я тестирую. Использование жестко закодированных светильников кажется ограниченным, потому что он проверяет только мой код с очень конкретными случаями, которые я себе представлял. (По общему признанию, мое воображение также ограничивает, в каких случаях я тестирую.)
Я использую тестирование как форму документации для кода. Если у меня есть жестко закодированные значения прибора, трудно показать, что пытается продемонстрировать конкретный тест. Например:
describe Item do describe '#most_expensive' do it 'should return the most expensive item' do Item.most_expensive.price.should == 100 # OR #Item.most_expensive.price.should == Item.find(:expensive).price # OR #Item.most_expensive.id.should == Item.find(:expensive).id end end end
Используя первый метод не дает читателю никаких указаний на то, что самый дорогой пункт является только то, что его цена составляет 100. Все три метода попросить читателя принять на веру, что прибор
:expensive
является самый дорогой, указанный вfixtures/items.yml
. Небрежный программист мог нарушить тесты, создавItem
вbefore(:all)
или вставив другое приспособление вfixtures/items.yml
. Если это большой файл, может потребоваться много времени, чтобы выяснить, в чем проблема.
Одна вещь, которую я начал делать это добавить метод #generate_random
для всех моих моделей. Этот метод доступен только тогда, когда я запускаю свои спецификации. Например:
class Item
def self.generate_random(params={})
Item.create(
:name => params[:name] || String.generate_random,
:price => params[:price] || rand(100)
)
end
end
(. Конкретные детали того, как я делаю это на самом деле немного чище у меня есть класс, который обрабатывает генерацию и очистку всех моделей, но этот код достаточно ясно для моего примера.) Поэтому в приведенном выше примере я могу проверить следующее. Предупреждение для финтого сердца: мой код в значительной степени зависит от использования before(:all)
:
describe Item do
describe '#most_expensive' do
before(:all) do
@items = []
3.times { @items << Item.generate_random }
@items << Item.generate_random({:price => 50})
end
it 'should return the most expensive item' do
sorted = @items.sort { |a, b| b.price <=> a.price }
expensive = Item.most_expensive
expensive.should be(sorted[0])
expensive.price.should >= 50
end
end
end
Таким образом, мои тесты лучше выявить удивительное поведение. Когда я генерирую данные таким образом, я иногда натыкаюсь на краевой случай, когда мой код не ведет себя так, как ожидалось, но который я бы не поймал, если бы использовал только приборы. Например, в случае с #most_expensive
, если я забыл обработать специальный случай, когда несколько предметов имеют самую дорогую цену, мой тест будет иногда терпеть неудачу на первых should
. Видя, что не-детерминированные сбои в AutoSpec подскажут мне, что что-то не так. Если бы я использовал только приборы, может потребоваться гораздо больше времени, чтобы обнаружить такую ошибку.
Мои тесты также дают немного лучшую работу, демонстрируя в коде, каково ожидаемое поведение. В моем тесте ясно, что отсортированный массив отсортирован по убыванию в порядке убывания. Поскольку я ожидаю, что #most_expensive
будет равен первому элементу этого массива, еще более очевидно, что ожидаемое поведение most_expensive
.
Итак, это плохая практика? Является ли мой страх перед светильниками иррациональным? Является ли метод generate_random
для каждой модели слишком большой работой? Или это работает?
Линия «3.times {@items 50})« не выглядит правильной. –
И теперь, всего через 58 месяцев, я отвечаю ... Это выглядит не так, потому что у него есть «< <» в нем ... но не удалось сбежать. – bobocopy