Rails 3.0.7 → Как вы можете быстрее запускать тесты?
Я запускаю mysql, database_cleaner, Rspec и т.д. У меня уже около 518 тестов, и они забирают 88 секунд. Это неприемлемо для меня, так как моя разработка приложений только начинается.
Итак, прежде чем идти дальше, я хотел бы попытаться найти способы сократить время, затрачиваемое на выполнение этих тестов, - надеюсь, без фактического изменения тестов.
В большинстве случаев я пытаюсь использовать заглушки. Однако, когда я тестирую модели и запросы, я использую базу данных.
Я думаю, что database_cleaner замедляет их, но я не знаю, как тестировать запросы и прочее без него.
Использование sqlite3 с опцией ": memory:", похоже, только сбривает около 10 секунд (вид неутешительного результата...)
Что я могу сделать, чтобы действительно ускорить мои тесты?
Ответы
Ответ 1
Райан Бруннер предложил большой совет. Все, что он сказал, правда в целом, но не применимо ко мне.
Я не упоминал Factory Girl, потому что я не думал упоминать об этом (не спрашивайте). Он оказался очень уместным, потому что он отвечал за выполнение тестов настолько медленно.
Просто удалив Factory девушку полностью из тестов моего контроллера (я использовал Factory.build
), мне удалось свести их с 50 секунд к чему-то вроде 5.
Причина в том, что Factory.build
вызывает Factory.create
для ассоциаций, что приводит к удалению базы данных... поэтому, если у вас много ассоциаций, потребуется некоторое время создать новый объект модели. Но даже больше, что только в моем случае составляло 30-35% накладных расходов. Factory_girl на самом деле тратит 65-70% своего времени на создание не-базы данных. Я понятия не имею, почему, но после того, как каждый вызов будет Factory.build
, все равно потребуется некоторое время для создания моих объектов. Переход с базовым MyClass.new
оказался быстрее MUCH.
Весь мой комплект тестов теперь занимает чуть меньше 30 секунд, а не до 90 секунд. Это увеличение скорости на 300% в целом путем внесения этих изменений... но когда дело дошло до контрольных тестов, у меня было увеличение скорости на 2000%, и я уже успел укусить! Все служебные издержки были связаны с Factory.build
! Вот откуда взялась большая часть прибыли.
Конечно, я вернулся в свои модели и использовал Factory.build
или просто MyClass.new
везде, где мог.
Я также добавил :default_strategy => :build
в factories.rb
тоже, когда мог, чтобы Factory Girl не ударил базу данных. Если вы спросите меня, это должно быть по умолчанию, так как только 1 тест завершился неудачно в результате этого изменения, но мне удалось получить 10 целых секунд из моих модельных тестов только этим изменением.
Если у вас такие проблемы, как я, выполните следующие действия, и вы заметите улучшение скорости в 2-3 раза с небольшим недостатком.
Ответ 2
Существует множество стратегий, которые вы можете использовать, чтобы ускорить время тестирования. Если вы только начинаете, и вы видите 88-секундное время выполнения, я бы предположил, что к ним относится большое количество:
- Использовать spork. Spork выполнит всю загрузку, и среда потребует один раз, сохранит это в памяти и перезагрузит ваши тесты. Это может быть огромной помощью при быстром тестировании.
- Будьте умны о том, как вы тестируете. Лично мой рабочий процесс заключается в разработке тестов, запуске полного набора тестов, чтобы увидеть, что не удается, а затем запускать только новые/неудачные тесты, пока я не получу эти зеленый. Наконец, когда все закончится, я запустил пакет в последний раз, чтобы увидеть, что я еще что-то отрекся.
- Очистите свой Gemfile. Большая часть времени загрузки Rails затрачена на необходимость, и если у вас есть драгоценные камни, которые вы больше не используете, вы добавляете время загрузки без каких-либо преимуществ. Вытащите все, что вы не используете, и подумайте о том, чтобы вещи, которые использовались только в одном или двух местах в названной группе, поэтому вы можете потребовать их вручную во время выполнения (будьте осторожны с этим - вы торгуете начальной скоростью загрузки для выполнения запроса, что отлично подходит для dev, но дерьмово для производства).
- Будьте осторожны в том, что нужно заглушить и издеваться.. Если вы действительно выполняете модульные тесты, например, вам следует избегать касания базы данных в целом или, по крайней мере, для тестов вашего контроллера. Подумайте, какова ответственность класса на самом деле. Контроллер не несет ответственности за сохранение записей, и он отвечает за то, чтобы показать моделях сохранение записей. Даже модели не несут ответственности за сохранение вещей в базах данных, они несут ответственность за указание ActiveRecord на.
- Если вы хорошо разбираетесь в вещах, не считайте Rails. Вам нужно будет иметь почти все функциональные возможности ActiveRecord, которые были пропущены в ваших тестах, но если вы сможете это сделать, вы увидите значительное уменьшение времени тестирования (вероятно, более чем на порядок).
Ответ 3
Я использовал следующий хак, чтобы сократить время, затраченное на сборщик мусора:
http://makandra.com/notes/950-speed-up-rspec-by-deferring-garbage-collection
В статье упоминается улучшение на 15%, но в моих тестах я вижу около 25% с Ruby 1.9.2, Rails 3.0.x и RSpec 2.0.
Кроме того, если вы не используете автотест, это может помочь, поэтому вы выполняете только те тесты, которые изменили код.
Наконец, попробуйте использовать опцию RSpec "--profile", чтобы определить 10 самых медленных примеров и посмотреть, можете ли вы оптимизировать работу худших нарушителей; В одном из моих проектов оказалось, что только 3 из моих тестов удвоили время выполнения теста на 150 тестов, поэтому я "исправил" их, и он вернул весь тестовый пакет в приемлемый временной масштаб.
Ответ 4
Вам нужно постоянно запускать все ваши тесты? Вы можете настроить задачи рейка для разных наборов тестов и просто запускать те, которые имеют отношение к частям приложения, которое вы недавно изменили. Конечно, большинство тестов будут выполняться на коде, который не изменился. Затем каждый раз вы можете запускать полный набор спецификаций, чтобы убедиться, что все совместимо. В любом случае это похоже на самое легкое решение.
Ответ 5
Я думаю, что если вы включите use_transactional_fixtures в RSpec, вам вообще не нужно будет использовать database_cleaner.
Кроме того, рассмотрите возможность использования NullDB, чтобы избежать попадания в реальную базу данных, за исключением случаев, когда вам приходится (мой подход никогда не ударять по базе данных в модульных тестах, только в тестах интеграции).
Ответ 6
Посмотрите на параллельное тестирование с помощью Hydra
:
http://logicalfriday.com/2011/05/18/faster-rails-tests-with-hydra/
Посмотрите также:
http://www.adomokos.com/2011/04/running-rails-rspec-tests-without-rails.html