Как вы ускоряете тесты Java? - PullRequest
29 голосов
/ 18 июня 2009

В настоящее время наш проект имеет более 3000 модульных тестов, и «ant testAll» занимает более 20 минут. кроме улучшения аппаратного обеспечения, есть ли способы ускорить процесс?

Ответы [ 16 ]

29 голосов
/ 18 июня 2009

Так же, как вы бы ускорили любой другой код. Узнайте, какие тесты занимают больше всего времени, и посмотрите, как их можно оптимизировать.

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

Время ваших тестов. Как правило, 90% из них будут выполняться почти мгновенно, а последние 10% - почти все время. Найдите эти 10% и посмотрите, что они делают.

Запустите код через профилировщик и обратите внимание, на что тратится время. Гадание - пустая трата времени. То, что вы думаете , что делает бегун, бессмысленно. Вместо того, чтобы гадать, выясните , что он делает. Тогда ты будешь знать, как это ускорить.

14 голосов
/ 18 июня 2009

Некоторые идеи:

  • Используйте фальшивый фреймворк, чтобы избежать попадания в базы данных (или звонки через веб-сервис и т. Д.).
  • Если вы выполняете ту же или аналогичную настройку для множества отдельных тестов, попробуйте сделать это в настройках тестового прибора (то есть, что-то, что делается один раз для каждого прибора, а не один раз для теста).
  • Я считаю, что некоторые тестовые среды позволяют вам выполнять тесты параллельно
10 голосов
/ 18 июня 2009

Вы можете разделить свои юнит-тесты на комплекты. Является ли ваше приложение модульным? Как часто вам действительно нужно запускать все тесты? Было бы приемлемо, если бы ваши разработчики запускали только модульные тесты, относящиеся к их собственному модулю, а массив тестов выполнялся ночью и / или на КИ?

Существуют ли какие-либо особые модульные тесты, которые являются очень сложными (я знаю, что здесь я использую функциональные и интеграционные тесты, но линия иногда нечеткая), но может ли любой из них быть запущен на здравом уме -уровень во время разработки, а затем его полный запуск на CI?

Редактировать : просто для краткости я кратко опишу тестовые процедуры в одном из моих предыдущих проектов

Прежде всего, рост тестовой системы был органичным, то есть изначально он не планировался, а был изменен и изменен по мере роста. Следовательно, оно не было совершенным, и были некоторые соглашения об именах, которые со временем стали апокрифическими.

  • На уровне разработчика мы использовали простой двухминутный набор тестов под названием CheckIn, который подтвердил, что код достаточно здоров, чтобы присоединиться к магистрали.
  • Кроме того, мы постоянно проводили тесты на работоспособность на машине CI. Это были упрощенные версии более сложных интеграционных и функциональных тестов, все модульные тесты и все регрессионные тесты.
  • Комплексные тестовые наборы (по количеству часов) выполнялись дистанционно днем ​​и ночью, а результаты компилировались на следующее утро.

Автоматическое тестирование - это гайки дурака.

5 голосов
/ 18 июня 2009

Для начала:
a) Получите статистику о времени выполнения ваших тестов Junit. Возможно, вы уже вносите эту информацию в свои отчеты о тестах.
б) Выйти из 10 лучших тестовых классов (вовремя) и попытаться сократить время. Это нужно делать на постоянной основе.
c) Постарайтесь сократить время выполнения путем рефакторинга или даже изменения подхода к тестированию.
Один такой случай, с которым я столкнулся, находится в одном классе Test для тестовых случаев CRUD. Тестовый случай обновления сначала создавал funtionlaity, а затем обновлял. Но мы уже были протестированы create in seprate test case. Так что в этих случаях вы можете связать свои тестовые примеры как

@Test()
    public void testCreate() throws Exception
    {}
    @Test(dependsOnMethods = "testCreate")
    public void testAmend() throws Exception
    {}
    @Test(dependsOnMethods = "testAmend")
    public void testDelete() throws Exception
    {} 

Таким образом, вы экономите на тестировании дубликатов.

d) Был еще один случай, когда мне удалось значительно сократить время. У нас была система (inherietd), в которой каждый тестовый случай вызывал SetUp (сервер Strating Spring и т. Д.) И после запуска отключал системные ресурсы. Это было очень много времени, поэтому я реорганизовал его для запуска ресурсов coomon перед тестовым набором и после того, как весь пакет был Сделано, затем закройте их.

e) В зависимости от вашего проекта могут быть и другие узкие места, которые вам могут понадобиться.

Как управлять временем сборки в TDD

4 голосов
/ 18 июня 2009

Используете ли вы fork="yes" в вашем junit звонке? Если это так, убедитесь, что вы установили forkMode="once", в противном случае задача junit запустит новую виртуальную машину для каждого класса TestCase. С 3000 юнит-тестами это будет иметь решающее значение.

http://ant.apache.org/manual/Tasks/junit.html

4 голосов
/ 18 июня 2009

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

Если это так, один из подходов заключается в многопоточности вашего тестового прогона. Test-NG очень хорошо это поддерживает. Преобразование ваших тестов из junit в test-ng не так сложно и нужно сделать только один раз.

Тесты, которые должны выполняться последовательно, могут быть легко помечены:

@Test(sequential = true)
public class ATest {
  ...

На многоядерной машине вы увидите огромные улучшения во время выполнения. Даже на одном ядре вы увидите хорошее улучшение, поскольку некоторые потоки ожидают операций ввода-вывода.

Подробнее о настройке см. Здесь:

http://beust.com/weblog/archives/000407.html

Надеюсь, это поможет.

....

Еще несколько предложений - Не могу поверить, что вы не используете непрерывную интеграцию. Поверьте мне, 30 разработчиков не собираются перегружать ваш CI сервер. Даже если вы не можете купить CI, установите hudson на свою машину - установка займет 10 минут, а преимущества огромны. Спросите своего менеджера, который хуже каждого разработчика, ожидающего завершения модульных тестов или того, чтобы сервер сделал это за вас. Наличие глупой шляпы для человека, который сломал сборку, обычно достаточно, чтобы убедить разработчиков запустить свои юнит-тесты.

Если качество проверок действительно является серьезной проблемой (не забывайте, что регистрация всегда может быть отменена), рассмотрите Teamcity - он запускает тесты и не фиксирует код, если тесты терпит неудачу.

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

Но помните, что умные инструменты, такие как test-ng, teamcity и clover, покажутся вам только хорошими - хорошие тесты не напишут сами!

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

  1. Оптимизация тестов - макеты, общие настройки и т. Д.
  2. Выполнить тесты параллельно
  3. Получите что-то еще для запуска тестов - сделайте это автономной задачей, используя Hudson или аналогичный
  4. Запускайте только те тесты, которые необходимо выполнить - сортируйте их по пакетам или используйте клевер и бамбук.
3 голосов
/ 18 июня 2009

Очевидно, что в вашем тесте что-то занимает много времени.

Иногда вы не можете обойти медленный тест. Например, тестирование Spring может читать все его файлы конфигурации, тестирование работы отображения гибернации и тому подобное. Хорошая вещь об этих тестах состоит в том, что они должны быть запущены только в одном тесте, и тогда вы можете макетировать все это, но вы также можете решить запустить их как часть интеграционного теста и позволить серверу сборки беспокоиться об этом. 1003 *

Остальные тесты медленные либо потому, что они выполняют ввод-вывод, либо потому, что они чрезмерно связаны с процессором.

IO может быть много вещей. Вызовы веб-службы и базы данных могут быть абстрагированы и смоделированы, и несколько реальных вызовов, которые вам нужно сделать, могут быть перенесены в фазу интеграции, если это будет необходимо. Ведение журнала также может сильно замедлить работу, особенно при использовании 3000 тестовых случаев. Я бы сказал, просто отключите ведение журнала и положитесь на свой мозг и отладчик, если тест не пройден.

Могут быть случаи, когда сам IO является тестируемым модулем. Например, если вы тестируете часть сервера базы данных, которая записывает данные таблицы на диск. В этом случае попытайтесь сохранить как можно больше ввода-вывода в памяти. В Java многие из абстракций ввода / вывода имеют реализации в памяти.

Тесты на привязку к ЦП также бывают разных. Чистая проверка производительности и пропускной способности должна находиться на этапе интеграционных испытаний. Если вы раскручиваете кучу потоков, чтобы проверить ошибку параллелизма, вы можете переместить большой тест на фазу интеграции и сохранить «облегченную» версию в своем обычном наборе тестов.

И, наконец, профилировщик - ваш друг. Вполне возможно, что части вашего кода можно сделать более эффективными и заметно ускорить ваши тесты.

2 голосов
/ 18 июня 2009

Ну, я не знаю, что делает ваш юнит-тест, но вам нужно спросить себя почему это занимает 20 минут. Из моего опыта часто бывает много тестов, которые выполняются в течение нескольких миллисекунд, и несколько тестов, которые составляют оставшееся время. Чаще всего это тесты с участием IO / network / DB-Stuff. Например, если вы тратите много времени на ожидание из-за задержки в сети, вы можете рассмотреть возможность параллельного выполнения ваших тестов.

Вы можете выследить эти тесты и искать улучшения. Но ускорение ваших тестов не делает ваш реальный код лучше. Возможно, вы захотите посмотреть на тесты, которые требуют много времени, потому что тестируемый класс не является оптимальным. Определение и улучшение подобных ситуаций, скорее всего, также сделает ваш продукт лучше / быстрее.

2 голосов
/ 18 июня 2009

Без дальнейшего знания того, что тестируется, есть только два подхода, которые легко представить:

  • используйте лучшее оборудование (извините)
  • упростить логику теста

Возможно, вы захотите даже запустить профилировщик поверх тестового прогона и посмотреть, есть ли какие-либо особенно неэффективно реализованные тесты.

1 голос
/ 19 июня 2009

Вот подход, который я бы выбрал.

  1. Просмотрите ваши контрольные примеры, поищите лишние тесты.С 3000 испытаний, скорее всего, вы двойные и пятикратные покрытия частей, которые не должны быть.
  2. Выберите ваши "канарейки".Это тесты, которые вы хотите всегда выполнять, те, которые устранят опасность в других частях.Скорее всего, это тестовые сценарии более высокого уровня, которые тестируют интерфейсы общедоступного API, используемые между компонентами.В случае сбоя одного из них вы можете зайти и запустить полный набор тестов для компонента.
  3. Начните миграцию на платформу, подобную TestNG , и начните классифицировать ваши тестовые случаи, затем выполните только классификацию того, над чем вы работаете, с ночными полными тестами.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...