Как вы масштабируете интеграционное тестирование?

21

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

При более чем 200 интеграционных тестах мы уже достигли отметки в 1 час, чтобы завершить полный тестовый запуск (на настольном компьютере разработчика), и это отрицательно сказывается на способности разработчика допустить запуск всего пакета как части рутинных push-процессов. Что влияет на мотивацию, чтобы быть дисциплинированным о создании их хорошо. Мы тестируем интеграцию только ключевых сценариев спереди назад, и мы используем среду, которая отражает производство, которая создается с нуля каждый тестовый прогон.

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

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

Чтобы было ясно, попытаться не допустить, чтобы это стало обсуждением модульного тестирования или интеграционного тестирования (которое никогда не должно продаваться). Мы проводим как модульное тестирование с TDD, так и интеграционное тестирование в этом продукте. Фактически, мы проводим интеграционное тестирование на различных уровнях архитектуры сервисов, которые у нас есть, где это имеет смысл для нас, так как нам необходимо проверить, где мы вносим серьезные изменения при изменении шаблонов в нашей архитектуре на другие области система. 

Немного о нашем техническом стеке. В настоящее время мы проводим тестирование в среде эмуляции (с интенсивным использованием процессора и памяти), чтобы выполнить наши тесты от начала до конца. Он состоит из веб-сервисов Azure REST, входящих в бэкэнд noSql (ATS). Мы моделируем нашу производственную среду, запустив эмулятор рабочего стола Azure + IISExpress. Мы ограничены одним эмулятором и одним локальным внутренним хранилищем для каждой машины разработчика.

У нас также есть облачный CI, который выполняет тот же тест в той же эмулируемой среде, а тесты в два раза дольше (2 часа +) в облаке с нашим текущим поставщиком CI. Мы достигли пределов SLA провайдеров облачных CI с точки зрения производительности оборудования и превысили их допуск во время выполнения теста. Чтобы быть справедливым для них, их характеристики не плохие, но, очевидно, вдвое хуже, чем настольный компьютер.

Мы используем стратегию тестирования - перестройку нашего хранилища данных для каждой логической группы тестов и предварительную загрузку тестовых данных. Несмотря на всестороннюю гарантию целостности данных, это добавляет 5-15% влияния на каждый тест. Поэтому мы думаем, что оптимизировать эту стратегию тестирования на данном этапе разработки продукта мало что можно. 

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

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

  • Написание меньшего количества тестов не вариант. Давайте не будем спорить, что в этой теме.
  • Использование более быстрого оборудования, безусловно, вариант, хотя и очень дорогой.
  • Параллельное выполнение групп тестов / сценариев на отдельном оборудовании также определенно является предпочтительным вариантом.
  • Создание группы тестов на основе разрабатываемых функций и сценариев является правдоподобным, но в конечном итоге не является надежным для доказательства полного охвата или уверенности в том, что изменения не повлияют на систему. 
  • Технически возможен запуск в облачной промежуточной среде вместо запуска в эмуляторе рабочего стола, хотя мы начинаем добавлять время развертывания к тестовым прогонам (~ 20 минут каждый в начале тестового прогона для развертывания материала).
  • Разделение компонентов системы на независимые логические составляющие в некоторой степени правдоподобно, но мы ожидаем, что пробег будет ограниченным, поскольку ожидается, что взаимодействия между компонентами со временем возрастут. (то есть изменение может повлиять на других неожиданным образом - как это часто случается, когда система развивается постепенно)

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

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

[Обновление: 16.12.2016: В итоге мы инвестировали больше средств в параллельное тестирование CI для обсуждения результатов: http://www.mindkin.co.nz/blog/2015/12/16/16-jobs]

Джезз Сантос
источник
С момента написания этой статьи я исследовал, что nCrunch (который мы широко используем для нашего модульного тестирования) может быть инструментом, который может предложить нам тактику. Очевидно, у него есть возможность отправлять тесты на удаленные машины и запускать их параллельно. Итак, может быть стоит попробовать определить группы интеграционных тестов, а также несколько экземпляров высокопроизводительных облачных машин? nCrunch утверждает, что именно это и было задумано. Кто-нибудь еще пробовал это?
Jezz Santos
Похоже, что это погружение в дискуссию о том, что такое, а что нет, и интеграционное тестирование, и недопонимание людьми модульного тестирования и интеграционного тестирования, о, мальчик!
Джезз Сантос

Ответы:

9

Я работал в месте, которое занимало 5 часов (на 30 машинах) для проведения интеграционных тестов. Я реорганизовал кодовую базу и вместо этого провел юнит-тесты для нового материала. Модульные испытания заняли 30 секунд (на 1 машине). Ох, и ошибки тоже исчезли. И время разработки, так как мы точно знали , что ломалось с гранулярными тестами.

Короче говоря, нет. Полные интеграционные тесты растут в геометрической прогрессии по мере роста вашей кодовой базы (чем больше кода, тем больше тестов, а чем больше кода, тем больше времени требуется для выполнения всех тестов, так как требуется больше «интеграции»). Я бы сказал, что все, что находится в диапазоне «часов», теряет большинство преимуществ непрерывной интеграции, поскольку петли обратной связи не существует. Даже улучшения на порядок недостаточно для того, чтобы вы были хорошими, и это далеко не так, чтобы вы могли масштабироваться.

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

Telastyn
источник
Я согласен. Модульные тесты гораздо более масштабируемы и поддерживают более быструю обратную связь.
Брэндон
8
Возможно, вы упустили этот момент. OP уже проводит обширное тестирование uint, а также тестирование интеграции. Модульные тесты никогда не заменяют интеграционные тесты. Разный инструмент, разные практики, разные цели, разные результаты. Это никогда не вопрос того или другого.
Джезз Сантос
1
Добавил ясность в пост, чтобы четко заявить, что мы строим этот продукт с использованием TDD, поэтому у нас уже есть тысячи модульных тестов, подкрепленных рассматриваемыми интеграционными тестами. ,
Джезз Сантос
8

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

Учитывая, что вы уже запускаете что-то в облаке, мне кажется, что вы в лучшем положении, чтобы масштабировать свои тесты на нескольких машинах.

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

Ewan
источник
Хорошая идея! рассматривая такую ​​стратегию, но с некоторыми инструментами, которые помогают распределенному тестированию
Jezz Santos
4

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

Я столкнулся с подобной проблемой, но не в наших интеграционных тестах (они запускались за считанные минуты). Вместо этого это было просто в наших сборках: крупномасштабная кодовая база C, собирала бы часы.

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

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

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

Сторонний проект в ожидании, интеграция позже

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

Разбор исходных файлов вручную, чтобы выяснить, что перестраивать / перезапускать

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

Та же стратегия должна быть применима к интеграционным тестам. Просто рекурсивно анализируйте исходные файлы, чтобы выяснить, от каких файлов зависят интеграционные тесты (например:import в Java,#includeв C или C ++) на стороне сервера, и файлы, включенные / импортированные из этих файлов и т. д., строят полный граф файлов зависимостей включения / импорта для системы. В отличие от синтаксического анализа сборки, который формирует DAG, график должен быть ненаправленным, поскольку он заинтересован в любом измененном файле, который содержит код, который может быть выполнен косвенно *. Повторно запускайте интеграционный тест, только если какой-либо из этих файлов на графике для интересующего интеграционного теста изменился. Даже для миллионов строк кода этот анализ было легко выполнить менее чем за минуту. Если у вас есть файлы, отличные от исходного кода, которые могут повлиять на интеграционный тест, например файлы содержимого, возможно, вы можете записать метаданные в комментарии в исходном коде, указывающие на эти зависимости в интеграционных тестах, так что в случае изменения этих внешних файлов тесты также получить повторно.

* Например, если test.c включает в себя foo.h, который также включен в foo.c, то изменение test.c, foo.h или foo.c должно пометить интегрированный тест как требующий нового запуска.

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


источник
3

Похоже, у вас слишком много интеграционных тестов. Напомним, тестовая пирамида . Интеграционные тесты относятся к середине.

В качестве примера возьмем репозиторий с методом set(key,object),get(key) . Этот репозиторий широко используется в вашей кодовой базе. Все методы, которые зависят от этого хранилища, будут протестированы с поддельным хранилищем. Теперь вам нужно только два интеграционных теста, один для набора и один для получения.

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

Интеграционные тесты должны проверять правильность ORM, репозиториев и абстракций очереди. Как правило, для тестирования интеграции не требуется код домена - только абстракции.

Почти все остальное может быть проверено модульно с использованием заглушек / насмешек / подделок / in-mem-реализаций для зависимостей.

Эсбен Сков Педерсен
источник
1
Интересная перспектива. Наши интеграционные тесты не пытаются проверить каждую перестановку каждого параметра каждого вызова ReST. На наш взгляд, это не интеграционное тестирование. Они запускают ключевые сквозные сценарии через API, которые, в свою очередь, затрагивают различные бэкэнд-магазины и другие системы. Цель состоит в том, чтобы гарантировать, что по мере изменения API они идентифицируют, какие сценарии требуют внимания (т.е. больше не работают должным образом).
Джезз Сантос
1
У нас есть интеграционные тесты на разных уровнях архитектуры. В вашем примере у нас есть модульные тесты для классов, которые обращаются к хранилищу данных, поэтому мы знаем, что они правильно обращаются к нашему хранилищу данных, у нас есть интеграционные тесты для настройки копии наших хранилищ и проверки их правильного чтения и записи данных. с магазином. Затем мы используем эти классы данных в REST API, который мы создаем с помощью модульных тестов, а затем интеграционных тестов, которые запускают веб-сервис и обращаются к нему, чтобы убедиться, что данные поступают на всем пути от начала до конца и наоборот. Вы предлагаете, чтобы у нас было слишком много тестов здесь?
Джезз Сантос
Я обновил свой ответ как ответ на ваши комментарии.
Эсбен Сков Педерсен
2

По моему опыту в среде Agile или DevOps, где распространены конвейеры непрерывной доставки, интеграционное тестирование должно проводиться после завершения или настройки каждого модуля. Например, во многих средах с непрерывной конвейерной доставкой нередки случаи, когда на одного разработчика приходится несколько развертываний кода в день. Выполнение быстрого набора интеграционных тестов в конце каждого этапа разработки перед развертыванием должно быть стандартной практикой в ​​среде такого типа. Для получения дополнительной информации, отличная электронная книга для включения в ваше чтение по этому предмету - Практическое руководство по тестированию в DevOps. , написанное Катриной Клоки.

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

Другой вариант, который стоит рассмотреть, - это решение, первоначально разработанное в 2002 году, под названием Service Virtualization. Это создает виртуальную среду, имитирующую взаимодействие модуля с существующими ресурсами для целей тестирования в сложных корпоративных DevOps или Agile-среде.

Эта статья может быть полезна, чтобы понять больше о том, как сделать интеграционное тестирование на предприятии

Дэн
источник
Хотя это может сработать (если система может быть разделена на такие модули, но не все продукты могут это сделать), это было нормой некоторое время назад, но фактически задерживает интеграцию, теряя все преимущества CI / CD. В некотором роде противодействующие, вы не думаете? Проблемы, обнаруженные в таком интеграционном тестировании, не могут быть легко и быстро сопоставлены с конкретным коммитом, поэтому требуют полного, с нуля расследования, как ошибки, поступающие с производства (и вы знаете, насколько дороже их исправить).
Дан Корнилеску
1

Вы измерили каждый тест, чтобы увидеть, где берется время? А затем, измерил производительность кодовой базы, если есть особенно медленный бит. Является ли общая проблема одним из тестов или развертывания, или обоих?

Обычно вы хотите уменьшить влияние интеграционных тестов, чтобы минимизировать их выполнение при относительно небольших изменениях. Затем вы можете оставить полный тест для прогона «QA», который вы выполняете, когда ветка переводится на следующий уровень. Таким образом, у вас есть модульные тесты для ветвей разработки, выполняются сокращенные интеграционные тесты при объединении и полный интеграционный тест при объединении с веткой-кандидатом на выпуск.

Таким образом, это означает, что вам не нужно перестраивать, переупаковывать и заново развертывать все при каждом коммите. Вы можете организовать свою настройку в среде разработчика, чтобы выполнить как можно более дешевое развертывание, полагая, что все будет в порядке. Вместо того, чтобы раскручивать целую ВМ и развертывать весь продукт, оставьте ВМ со старой версией на месте и скопируйте, например, новые двоичные файлы (YMMV в зависимости от того, что вам нужно сделать).

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

У нас была похожая проблема при запуске инструмента статического анализа на основе безопасности. Полные запуски занимали бы много времени, поэтому мы перешли от запуска коммитов разработчика к коммиту интеграции (т. Е. У нас была система, в которой dev сказал, что они закончили, она была объединена с веткой 'level 2', где проводилось больше тестирования, включая perf тесты. Когда это было завершено, оно было объединено с ветвью QA для развертывания. Идея состоит в том, чтобы удалять регулярные прогоны, которые будут происходить непрерывно, с прогонами, проводимыми ночью - разработчики получат результаты утром, и они не повлияют на их разработку. сосредоточиться до позже в их цикле разработки).

gbjbaanb
источник
1

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

Это, однако, создает новую проблему - разработчики не получают немедленной обратной связи, и сломанные сборки могут остаться незамеченными. Чтобы это исправить, важно, чтобы они знали, что что-то постоянно ломается. Инструменты для создания уведомлений, такие как Catlight или TeamCity в трее могут быть весьма полезными.

Но будет еще одна проблема. Даже если разработчик видит, что сборка не работает, он может не поспешить проверить это. В конце концов, кто-то еще может это проверять, верно?

По этой причине эти два инструмента имеют функцию «расследование сборки». Он скажет, проверяет ли кто-нибудь из команды разработчиков исправление поврежденной сборки. Разработчики могут добровольно проверить сборку, и, пока это не произойдет, все в команде будут раздражены красным значком возле часов.

Алекс
источник
0

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

  • Большой проект должен быть разбит на более мелкие отдельные проекты, которые компилируются в библиотеки. Инструменты Java, такие как nexus, делают это легко.
  • Каждая библиотека должна реализовывать интерфейс. Это помогает заглушить библиотеку в высокоуровневых тестах. Это особенно полезно, если библиотека обращается к базе данных или внешнему хранилищу данных (например, к мэйнфрейму). В таких случаях перевод данных мэйнфрейма или базы данных в воспроизводимое состояние, вероятно, будет медленным и может быть невозможным.
  • Интеграционные тесты для каждой библиотеки могут быть комплексными, но запускать их нужно только тогда, когда будет зафиксирован новый источник библиотеки.
  • Интеграционные тесты более высокого уровня должны просто вызывать библиотеки и предполагать, что они идеальны.

Магазин Java, в котором я работаю, использует этот подход, и мы редко зацикливаемся на ожидании запуска интеграционных тестов.

kiwiron
источник
Спасибо, но я думаю, что у нас нет единого понимания цели и применения интеграционного тестирования в этом контексте. Возможно, вы совмещаете интеграционное тестирование с модульным тестированием.
Джезз Сантос
0

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

Вместо прямой фиксации своих изменений в ветке разработчики отправляют их в централизованную автоматизированную систему верификации, которая выполняет проверки и:

  • в случае успеха он автоматически фиксирует изменения в ветке
  • в случае неудачи уведомляет соответствующих отправителей о переоценке своих изменений

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

Одним из таких примеров является система стробирования на основе Gerrit / Zuul, используемая OpenStack .

Еще один - ApartCI ( отказ от ответственности - я его создатель и основатель компании, предлагающей его).

Дэн Корнилеску
источник