Ускорение тестирования RSpec в большом приложении Rails

91

У меня есть приложение Rails с более чем 2000 примерами в моих тестах RSpec. Излишне говорить, что это большое приложение, и есть что протестировать. Запуск этих тестов на данном этапе очень неэффективен, и, поскольку это занимает так много времени, мы почти готовы отказаться от их написания перед запуском новой сборки. Я добавил --profile в свой spec.opts, чтобы найти самые продолжительные примеры, и есть как минимум 10 из них, выполнение которых занимает в среднем 10 секунд. Это нормально для вас, экспертов RSpec? Неужели 10 секунд слишком много для одного примера? Я понимаю, что с 2000 примеров потребуется нетривиальное количество времени, чтобы все тщательно протестировать, но на данный момент 4 часа - это немного смехотворно.

Какие времена вы видите для своих самых продолжительных примеров? Что я могу сделать для устранения неполадок в моих существующих спецификациях, чтобы выявить узкие места и ускорить процесс. Здесь действительно поможет каждая минута.

рандомбиты
источник
1
Интеграционные тесты медленных тестов? Они попадают в дб? Если да, то как часто перезагружается БД и можно ли имитировать БД?
Калеб Педерсон,
1
Можете ли вы просто запустить часть спецификаций, относящуюся к той части, над которой вы работаете, вроде автотеста SeattleRB? У вас есть сервер непрерывной интеграции, который может запускать все тесты?
Эндрю Гримм
Помните также, что все относительно. Я слышал «grrr, наш тестовый набор занимает вечность» и 20 минут ... и 16-20 часов. Все в глазах смотрящего. 10 секунд для данного теста часто означают модульный тест, который стал интеграционным тестом, как указано ниже.
Майкл Даррант,
Предложение для такого рода проблемы: используйте perftools.rbвместе со своей тестовой средой, чтобы понять, на что уходит большая часть вашего времени. Возьмите 10 лучших звонков и постарайтесь их исключить / просмотреть. Затем повторяйте, пока не будете счастливы.
aledalgrande

Ответы:

118

10 секунд - очень долгое время для выполнения любого отдельного теста. Мне кажется, что ваша цель по спецификации одновременно выполняет как модульные, так и интеграционные тесты. Это типичная вещь, в которую попадают проекты, и на каком-то этапе вам нужно будет преодолеть этот технический долг, если вы хотите производить больше и быстрее. Есть несколько стратегий, которые могут помочь вам в этом ... и я порекомендую несколько, которые я использовал в прошлом.

1. Отдельный модуль от интеграционных тестов

Первым делом я бы отделил юнит от интеграционных тестов. Вы можете сделать это:

  1. Перемещение их (в отдельные папки в каталоге spec) - и изменение целей граблей
  2. Пометка их (rspec позволяет тегировать ваши тесты)

Философия гласит, что вы хотите, чтобы ваши регулярные сборки были быстрыми, иначе люди не будут слишком счастливы запускать их часто. Так что возвращайся на эту территорию. Обеспечьте быстрое выполнение регулярных тестов и используйте сервер непрерывной интеграции для запуска более полной сборки.

Интеграционный тест - это тест, который включает внешние зависимости (например, Database, WebService, Queue и некоторые утверждают, что FileSystem). Модульный тест просто проверяет конкретный элемент кода, который вы хотите проверить. Он должен работать быстро (возможно 9000 за 45 секунд), т.е. большая его часть должна выполняться в памяти.

2. Преобразование интеграционных тестов в модульные тесты

Если основная часть ваших модульных тестов меньше, чем ваш набор интеграционных тестов, у вас есть проблема. Это означает, что несоответствия начнут проявляться легче. Итак, начните создавать больше модульных тестов, чтобы заменить интеграционные тесты. Вот что вы можете сделать, чтобы помочь в этом процессе:

  1. Используйте фиктивный фреймворк вместо реального ресурса. Rspec имеет встроенную структуру фиксации.
  2. Запустите rcov в своем наборе модульных тестов. Используйте это, чтобы оценить, насколько тщательно ваш набор модульных тестов.

Как только у вас есть подходящие модульные тесты для замены интеграционного теста, удалите интеграционный тест. Повторное тестирование только ухудшает обслуживание.

3. Не используйте приспособления

Светильники - зло. Вместо этого используйте фабрику (машинист или фабричный робот). Эти системы могут создавать более адаптируемые графы данных и, что более важно, они могут создавать объекты в памяти, которые вы можете использовать, а не загружать вещи из внешнего источника данных.

4. Добавьте проверки, чтобы остановить превращение модульных тестов в тесты интеграции

Теперь, когда у вас есть более быстрое тестирование, пора поставить проверки, чтобы ПРЕКРАТИТЬ это снова.

Существуют библиотеки, которые обезьяны исправляют активную запись, чтобы выдавать ошибку при попытке доступа к базе данных (UnitRecord).

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

  1. Кто-то проверяет - чтобы никто не ленился
  2. Правильный TDD требует быстрой обратной связи. Медленные тесты делают все это болезненным.

5. Используйте другие библиотеки, чтобы решить эту проблему.

Кто-то упомянул spork (ускоряет загрузку набора тестов под rails3), hydra / parallel_tests - для параллельного запуска модульных тестов (на нескольких ядрах).

Вероятно, это следует использовать ПОСЛЕДНИМ. Ваша настоящая проблема полностью находится на шагах 1, 2, 3. Решите это, и вы сможете лучше использовать дополнительную инфраструктуру.

BlueFish
источник
Отличный ответ. Совершенно верно, что никакие продвинутые инструменты не могут помочь быстро запустить плохие тесты. Так что старайтесь писать только хорошие.
Юра Омельчук
5
Я не согласен с вашим третьим пунктом, что вы не должны использовать приспособления. При правильном использовании они быстрее, чем фабрики, так как вам не нужно создавать объекты. Ваши данные есть, вам не нужно создавать их для каждого тестового примера.
wallerjake
3
Насчет того, что фабрики быстрее работают, - шутка, правда?
Майк Шиндель
Ускорить тесты по Избежание Factory селективно Girl - robots.thoughtbot.com/...
geekdev
16

Чтобы получить отличную книгу по повышению производительности вашего набора тестов, ознакомьтесь с презентацией Grease Your Suite .

Он документирует 45-кратное ускорение времени выполнения набора тестов за счет использования таких методов, как:

маршалл
источник
Мне нравятся ссылки, но не презентация. Каталожные карточки для чьего-либо выступления - это подсказка оратору, чтобы он смог заполнить суть презентации.
Брайан Мальтзан
Эта презентация больше не работает. fast_context ускоряет несколько блоков shoulda. quickerclip (эта ссылка также мертва), очевидно, ускоряет Paperclip. Hydra устарела для parallel_tests и Gorgon. В bootnap есть настройки файловой системы, и теперь он включен в Rails. В последних версиях Ruby улучшено выделение памяти и сборщик мусора.
Rep
5

Вы можете использовать Spork. Имеет поддержку 2.3.x,

https://github.com/sporkrb/spork

или ./script/spec_server, который может работать для 2.x

Также вы можете редактировать конфигурацию базы данных (что существенно ускоряет запросы к базе данных и т. Д.), Что также повысит производительность тестов.

Ришав Растоги
источник
Spork потрясающий. Он также работает на Rails 3 с небольшим взломом. Но одна вещь, которую следует отметить в Spork on Rails 3, заключается в том, что он, похоже, не улавливает изменения в контроллерах, поэтому вам нужно будет время от времени перезапускать его. Но в любом случае Spork действительно классный, прирост скорости тестов очень существенный.
AboutRuby 08
4
Spork предназначен только для ускорения запуска, а не для тестов. Так что при большом количестве спецификаций преимущество незначительное.
Reactormonk
Вопреки приведенным выше комментариям, spork можно использовать для ускорения тестов, а также запуска в соответствии с railscast railscasts.com/episodes/285-spork . Существенно улучшил мой набор тестов rspec для очень большого приложения. (Уменьшено со 192 секунд до 10 секунд!)
jamesc
3

10 секунд на пример кажется очень большим сроком. Я никогда не видел спецификации, которая занимала бы больше одной секунды, а большинство - гораздо меньше. Вы тестируете сетевые соединения? БД пишет? Файловая система пишет?

По возможности используйте макеты и заглушки - они намного быстрее, чем написание кода, который попадает в базу данных. К сожалению, для написания насмешек и заглушек требуется больше времени (и их труднее сделать правильно). Вы должны сбалансировать время, затрачиваемое на написание тестов, и время, затрачиваемое на выполнение тестов.

Я повторяю замечание Эндрю Гримма о рассмотрении системы CI, которая может позволить вам распараллелить ваш набор тестов. Для чего-то такого размера это может быть единственное жизнеспособное решение.

зететический
источник
да, если это 10 секунд, я бы профилировал это и посмотрел, где узкое место
rogerdpack
1
У меня огромный проект, и запуск одного теста rspec занимает около 15-20 секунд.
ExiRe
2

Несколько человек упомянули Гидру выше. В прошлом мы использовали его с большим успехом. Недавно я задокументировал процесс запуска и запуска гидры: http://logicalfriday.com/2011/05/18/faster-rails-tests-with-hydra/

Я согласен с мнением, что этот вид техники не следует использовать вместо написания тестов, которые по умолчанию хорошо структурированы и работают быстро.

Родригез
источник
0

Камень fast_require может вам помочь. Кроме того, ваш единственный способ (как вы это сделали) профилировать и оптимизировать, или использовать spork, или что-то, что позволяет параллельно запускать ваши спецификации. http://ruby-toolbox.com/categories/distributed_testing.html

Роджердпак
источник
0

Вы можете следовать нескольким простым советам, чтобы сначала выяснить, на что тратится большая часть времени, если вы еще не пробовали их. Взгляните на статью ниже:

https://blog.mavenhive.in/7-tips-to-speed-up-your-webdriver-tests-4f4d043ad581

Я предполагаю, что большинство из них являются общими шагами, которые применимы независимо от инструмента, используемого для тестирования.

Джейк
источник
-4

Удалите существующий набор тестов. Будет невероятно эффективно.

теданотто
источник