Это плохая практика для случайного генерирования тестовых данных? - PullRequest
6 голосов
/ 12 марта 2009

Поскольку я начал использовать rspec, у меня возникла проблема с понятием приборов. Мои основные проблемы следующие:

  1. Я использую тестирование, чтобы выявить удивительное поведение. Я не всегда достаточно умен, чтобы перечислять все возможные варианты для примеров, которые я тестирую. Использование жестко закодированных приборов кажется ограничивающим, потому что это тестирует мой код только с очень конкретными случаями, которые я вообразил. (По общему признанию, мое воображение также ограничивает относительно того, какие случаи я проверяю.)

  2. Я использую тестирование как форму документации для кода. Если у меня есть жестко запрограммированные значения приборов, трудно выявить, что пытается продемонстрировать конкретный тест. Например:

    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 метода для каждой модели - это слишком много работы? Или это работает?

Ответы [ 12 ]

0 голосов
/ 12 марта 2009
0 голосов
/ 12 марта 2009

Одна проблема со случайно сгенерированными контрольными примерами состоит в том, что проверка ответа должна вычисляться кодом, и вы не можете быть уверены, что в нем нет ошибок:)

...