[Перевод] Не автоматизируйте test cases
Как прямая автоматизация тест кейсов приводит к громоздким и раздутым наборам автотестов, которые практически не приносят пользы.
Общепринятой практикой в индустрии является использование тест кейсов в качестве основы для автоматизации тестирования. QA инженеры разрабатывают их на основе user stories в рамках обычного тестирования, а затем автоматизируют эти тесты. С каждой итерацией тестируется больше историй, автоматизируется больше тестовых случаев, и набор автоматических тестов становится всё больше. Руководители продвигают такие метрики, как, например, «процент покрытия» и хвалят команды с высокими показателями. Некоторые компании даже специально нанимают «инженеров по автоматизации», чья единственная работа состоит в том, чтобы брать тест кейсы и автоматизировать их.
К сожалению, автоматизация тест кейсов и навязывание «процента покрытия» — это антипаттерн обеспечения качества, который неизбежно приводит к раздутым и сложным в обслуживании наборам тестов, которые приносят мало пользы. Хотя автоматизация имеет решающее значение для agile delivery, этот чрезмерно упрощенный подход «фабрики автоматизации» не является хорошим способом автоматизации тестирования.
В этой статье мы продемонстрируем, почему «фабрики автоматизации» неэффективны и опишем более правильный подход к автоматизации, который гарантирует, что автоматизация тестирования поддерживает и ускоряет скорость разработки.
Издержки и преимущества автоматизации тестирования
Чтобы понять, почему автоматизация существующих тест кейсов настолько проблематична, нам нужно вернуться назад и немного проанализировать теорию автоматизации. В частности, нам нужно изучить издержки и преимущества автоматизации, посмотреть на ожидаемую ценность автоматизированных тестов во времени, а затем оценить, как ожидаемая ценность меняется в различных типах тестов. Затем мы сможем увидеть, как автоматизация тест кейсов с использованием простого подхода «фабрики автоматизации» повлияет на тестирование в целом.
Все автоматические тесты имеют два типа затрат: первоначальные затраты на разработку и текущие затраты на обслуживание. Ожидается, что автоматические тесты будут иметь некоторые преимущества — как минимум в виде разницы между временем выполнения теста вручную и (предполагаемой) гораздо более быстрой автоматической проверкой. Хотя есть и другие нематериальные преимущества (это веселее, обучает ценным навыкам и т. д.), не будем их здесь рассматривать.
Хотя это сильное упрощение, но оно описывает важные аспекты — каждый автоматизированный тест любого типа имеет как стоимость, так и ценность. Как эксперты по автоматизации, мы стараемся максимизировать преимущества и минимизировать затраты.
Вот некоторые вещи, которые влияют на стоимость теста:
наличие (или отсутствие) тестового фреймворка, в который будет добавлен тест
«чистота» существующего тестового фреймворка и тестовой модели
простота и возможность настройки тестового окружения (например, тестовых данных)
наличие приемлемого test oracle
неустойчивость интерфейсов или функций, с которыми будет взаимодействовать тест.
стабильность среды, в которой будет выполняться тест
технические навыки QA, необходимые для создания и обслуживания автотестов
Вот неполный список моментов, которые могут повлиять на объём преимуществ, которые мы получаем от автоматизации:
как часто запускаются тесты (каждый pull request, ежедневно, для каждого релиза и т. д.)
то как долго тест будет актуальным
насколько дорого (по времени или иным образом) проверять тот же тест вручную
как часто происходят ошибки при прохождении теста вручную
Оба эти списка неполные, и опытные инженеры, наверное, воскликнут: «А как же… х, y, z!» В данном случае, нет необходимости в исчерпывающем списке, чтобы показать, что каждый тест имеет целый ряд факторов, влияющих как на ожидаемые затраты, так и на ценность.
Мы уже установили, что затраты на автоматизацию можно разделить на первоначальную разработку и расходы на обслуживание. Преимущества также распределены по времени — ценность автоматизированного теста проявляется не сразу, она накапливается в течение всего срока службы теста. Таким образом, ценность не является окончательной в момент, когда тест был создан, а скорее меняется с течением времени.
Если бы мы построили график этого значения во времени для универсального автоматизированного теста, он выглядел бы примерно так:
На этом графике отображен тест, который изначально имеет отрицательную ценность: первоначальные затраты на разработку перевешивают преимущества в виде сэкономленного времени. Тем не менее, сэкономленное время в конечном итоге перекрывает первоначальные затраты, а также текущие расходы на техническое обслуживание, что обеспечивает положительную ценность. Каждый тест проходит определенный жизненный цикл, основанный на всех перечисленных выше этапах.
В зависимости от первоначальных затрат на разработку, затрат на техническое обслуживание и получаемых выгод тест может оказаться безубыточным и дать положительное значение намного раньше или, возможно, никогда не стать безубыточным:
На приведенном выше графике продемонстрирован тест, который изначально нёс некоторую ценность (после разработки линия устремляется вверх), но затем перестает приносить пользу. Возможно, этот тест перестал выполняться, или он был настолько ненадежным, что ему никто не доверял, или фичи, которую он тестировал, уже нет. Независимо от причин, мы видим, что лучше было бы вообще не автоматизировать этот тест.
Это ключевой момент: на ценность автоматизированных тестов влияют многие факторы, и в зависимости от этих факторов автоматические тесты могут иметь как положительную, так и отрицательную ценность с точки зрения общего сэкономленного времени.
Множество типов автоматических тестов
Далее давайте рассмотрим, как будут выглядеть графики зависимости ценности от времени для различных типов автоматических тестов. Под типами тестов мы подразумеваем все: от юнит тестов, более крупных «социальных» юнит тестов, ещё более крупных компонентных тестов, интеграционных тестов более высокого уровня, тестов, которые напрямую обращаются к API, тестов, которые мокают API и проверяют только пользовательский интерфейс, E2E-тесты и т. д.
Важно отметить, что в сложном современном программном обеспечении существует множество возможных типов автоматических тестов, существует почти бесконечное количество способов, которыми вы можете разложить систему на части и попытаться изолировать только ту или иную часть. Каждая из этих изоляций представляет собой возможный тип теста.
Изучение всех типов автоматизированных тестов, которые можно было бы создать для нетривиальной архитектуры, выходит за рамки этой статьи, но я бы рекомендовал «Практичную тестовую пирамиду» и «Стратегии тестирования для микросервисной архитектуры» в качестве хороших примеров. Чтобы сделать эту статью более краткой, мы рассмотрим только два крайних значения: небольшой юнит тест и большой полный сквозной E2E тест.
Как будет выглядеть уравнение затрат/выгод и график ценности во времени для юнит теста? Некоторые уникальные характеристики юнит тестов:
юнит тесты обычно можно написать за считанные минуты, если не секунды.
юнит тесты хорошо изолированы
юнит тесты могут выполняться за миллисекунды, наборы юнит тестов — за секунды.
юнит тесты, чаще всего, выполняются тысячи раз в день не только в CI/CD для каждого pull request, но и локально каждым разработчиком по мере написания кода.
даже при 100% покрытии юнит тесты не могут гарантировать, что приложение действительно работает так, как ожидалось, они проверяют приложение на «атомарном» уровне
Учитывая это, график ценности во времени юнит теста, вероятно, сильно отличается от нашего общего графика: он почти не требует первоначальных затрат, требует минимального обслуживания, и, хотя он выполняется постоянно, каждое инкрементное выполнение обеспечивает только очень небольшое количество ценности.
График будет выглядеть примерно так:
А как насчет крупнейшего из всех автоматических тестов — E2E? Как будет выглядеть для него график зависимости ценности от времени?
Некоторые ключевые особенности E2E тестов:
больше всего подвержены влиянию окружения и поэтому требуют тщательной стабилизации и контроля тестовых данных.
выполняются в полном тестовом окружении
часто включают множество (десятки, даже сотни) последовательных шагов.
самые медленные из всех тестов, некоторые тесты могут выполняться в течение нескольких минут.
обычно должны управлять функциональностью через пользовательский интерфейс.
обычно выполняются намного позже разработки — уже в CI/CD.
это единственный тип автоматизированных тестов, которые демонстрируют, что приложение работает именно так, как его использует пользователь.
С учетом этих особенностей график зависимости стоимости E2E от времени будет выглядеть примерно так:
На этом графике отражена значительно более высокая первоначальная стоимость, из-за чего ценность изначально была отрицательной. Тем не менее, регулярное выполнение этого теста с течением времени в конечном итоге позволяет ему выйти на уровень безубыточности, а затем перейти к положительному значению.
Опять же, ожидание того, что этот тест в конечном итоге даст положительный результат, зависит от того: как долго будет использоваться тест, как часто тест будет выполняться, насколько мы уверены в его надежности и результатах теста, насколько изменится тест при изменении пользовательского интерфейса, насколько стабильной является среда выполнения и т. д. Достижение безубыточности не гарантируется.
Где же автоматизировать
Природа E2E-тестов делает их создание и поддержку довольно дорогостоящими. Они сильнее подвержены проблемам с системным временем, синхронизациями, сетью или внешними зависимостями. Обычно они управляют некоторыми или всеми функциями через веб-браузер или интерфейс, предназначенный для использования человеком. Поскольку они выполняются на окружении максимально похожем на production, весьма вероятно, что этим тестам придется частично или полностью использовать это окружение совместно с другими тестами или пользователями, что может привести к конфликтам, сбоям и так далее.
Все эти причины (и многие другие) делают E2E тесты наиболее проблемными. Трудозатраты оправданы только из-за дополнительной ценности, которую они могут принести — только E2E тесты демонстрируют реальную работу полностью интегрированной системы.
Есть много других типов тестов, которые следует использовать, и чаще всего в сложных системах тесты более низкого уровня могут хорошо протестировать рассматриваемую функциональность, не неся затрат тестов более высокого уровня.
Другими словами: не пишите API тест, чтобы проверить часть логики, которую можно проверить в юнит тесте. Не создавайте E2E-тест для логики, которую можно проверить, напрямую вызвав API. Никогда не вносите в систему больше, чем нужно, чтобы продемонстрировать, что что-то работает. Определите логику или поведение, которое необходимо протестировать, и создайте тест, который изолирует именно это поведение. Используйте тесты более высокого уровня только для проверки интеграций.
Главный поинт заключается в том, что тесты E2E рискованны и редко являются лучшим типом автоматизации для тестирования конкретной функциональности. Обобщим: всегда предпочитайте те тесты, которые дают вам самое быстрое ожидаемое значение с наименьшими затратами.
Анализ затрат и ценности — это именно то, что привело к концепции тестовой пирамиды много лет назад. Пирамида демонстрирует, что, при прочих равных условиях, вы, как правило, хотите гораздо больше маленьких быстрых и дешевых тестов и гораздо меньше больших медленных и дорогих.
Хотя я не буду убеждать вас, что форма вашего набора тестов всегда должна быть пирамидой (Кент Доддс говорит, что это Трофей, Джеймс Бах предпочитает модель Круглой Земли, Джастин Сирлс говорит, что это просто абстракция), я надеюсь, что убедил вас в том, что любая автоматизация тестирования сопряжена с риском, и создание тестов на соответствующем уровне помогает снизить и контролировать этот риск. Огромная часть работы автоматизатора (в сотрудничестве с остальной частью команды!) заключается в том, чтобы точно определить, какой тип теста подходит и дает наибольшую вероятность положительной ценности теста в течении его жизненного цикла.
Другими словами: всякая автоматизация должна рассматриваться как инвестиция, в частности, рискованная инвестиция. Каждый тип автоматизированного тестирования представляет собой отдельный тип риска, и нам необходимо управлять общим риском и максимизировать ценность наших инвестиций, постоянно оценивая затраты и преимущества каждого типа.
«Фабрика автоматизации» и Top Heavy Suite
Если мы подумаем о user stories, критериях приемки и тест кейсах, которые специалист по обеспечению качества создал бы на их основе, как вы думаете, к какому типу автоматических тестов они естественным образом относятся? Если мы просто возьмем и автоматизируем их «в лоб», какой тип теста будет создан?
При общепринятых методах документирования Agile требования доносятся до тестировщиков на языке более высокого уровня, ориентированном на пользователя (часто мы даже называем их user stories).
Даже когда истории разбиты по горизонтали и описывают поведение определенного компонента (например, API REST), требования будут доноситься на «пользовательском языке» этого компонента. Таким образом, тестовые примеры, созданные на основе этой документации, естественным образом соответствуют более высоуровневым типам автоматизации тестирования.
Это главная проблема подхода «фабрики автоматизации» — автоматизация тестовых случаев неизбежно приводит к чрезмерному акценту на больших, медленных и дорогих тестах, потому что тестовые случаи, естественно, написаны на языке ручного тестировщика. Они точно соответствуют тому типу теста, который нам советует избегать наш анализ ценности!
Второй и иногда не менее мощный фактор, подталкивающий автоматизаторов к тому, чтобы предпочесть большие E2E-тесты более низкоуровневым тестам, заключается в том, что людям, не являющимся техническими специалистами (и некоторым техническим специалистам), психологически приятно знать, что тест кейсы были автоматизированы. Например, бизнес аналитики могут понять тестовые случаи, потому что они описывают функциональность приложения на языке, с которым они знакомы, и мысль о том, что эти случаи проверяются автоматически, успокаивают их, они надеются, что не будут получать звонки в 2 часа ночи от разгневанных пользователей. Они чувстсвуют гораздо меньше уверенности, когда им говорят, что система покрыта юнит тестами на 90%.
Таким образом, некоторые люди будут продвигать метрики «процента покрытия» и количества автоматических тестов, потому что это позволяет им чувствовать себя более комфортно, а не потому, что на самом деле это более эффективный метод автоматической проверки системы. Автоматизация каждого тест кейса может дать вам ощущение безопасности, но не создаст качественного набора тестов.
Симптомы «фабрики автоматизации»
Использование подхода «фабрики автоматизации» и прямой автоматизации test cases, приводит к некоторым очень болезненым, но, к сожалению, распространенным симптомам:
наборы тестов, выполнение которые занимает несколько часов или их возможно запустить только ночью
целые команды автоматизации, единственной целью которых является исследование ошибок предыдущего выполнения всех тестов, что занимает большую часть их времени.
падения тестов, когда единственное решение состоит в том, чтобы просто перезапускать тест, пока он не заработает.
удаление теста из конвейера CI/CD или его понижение до неблокирующего шага
тесты, которые разработчики избегают или прямо отказываются запускать их, потому что не доверяют их результатам.
пакеты с тысячами тестов, разбросанных по сотням папок (или даже разных репозиториев!), с дублированными тестами, закомментированными тестами и тестами, о которых никто не знает, что они делают и как они туда попали
титанические усилия инженеров автоматизаторов управлять раздутым набором тестов или скрывать сложность за слоем абстракций (например, Cucumber и т. д.)
Все эти симптомы указывают на то, что набор тестов не представляет ценности для команды, что довольно распространено. Похвально, что команды уделяли приоритетное внимание автоматизации тестирования в процессе разработки, но подошли к этому с наивным подходом «фабрики автоматизации».
Автоматизация здорового человека
Итак, как вам следует подходить к автоматизации тестирования, чтобы избежать этих проблем?
Потребность в автоматизации, покрывающей новые функции, следует оценивать комплексно, рассматривая варианты автоматизации для каждого типа тестирования. Вы не должны предполагать, что новый функционал обязательно нуждается в новом E2E-тесте. Вместо этого подумайте о том, как функциональность может быть наиболее эффективно покрыта полным набором всех типов тестов.
Вы не должны автоматизировать тест кейсы, вы должны автоматизировать функциональность. Функциональность может быть частично описана тест кейсом, но оборачивание каждого отдельного тест кейса в отдельный авто тест на том уровне, на котором его выполнял бы ручной тестер — не очень хорошая идея.
Наиболее эффективным способом тестирования новой функциональности может быть простое обновление существующего авто теста, перенос теста на более подходящий уровень или даже создание совершенно нового типа теста. Не забывайте, что по мере изменения функциональности системы вы должны удалять тесты так же часто, как и добавлять их!
В то время как тип необходимого вам теста будет сильно зависеть от архитектуры вашей системы, приемлемого уровня риска, существующих инструментов и т. д. В целом хороший подход к автоматизации будет выглядеть примерно так:
Добавляйте новые тесты на самом низком уровне.
Обновляйте существующие тесты, чтобы охватить новые функции.
Удаляйте все устаревшие или избыточные тесты или объедините их.
Протестируйте основной кейс на высоком уровне, а затем перенесите изменения этого теста в более мелкие, более низкоуровневые типы тестов.
Добавляйте новые E2E-тесты высокого уровня только в случае крайней необходимости.
Внедрите новый тип автоматизированного теста, если эта функциональность не может быть покрыта ни одним существующим типом теста.
Измените архитектуру системы, чтобы использовать новый тип автоматизированного тестирования.
Постоянно и критически оценивайте работоспособность полного набора всех типов тестов всей командой разработчиков.
Пункт номер 7 заслуживает особого внимания, так как это главное отличие между хорошими подходами к автоматизации и «фабрикой автоматизации».
Мы должны перестать думать об автоматизации тестирования как о чем-то, что применяется к программному обеспечению только после его создания. Вместо этого автоматизацию следует рассматривать как важнейшую часть самого процесса разработки программного обеспечения. Автоматизация должна развиваться с помощью программного обеспечения, и требования автоматизации должны определять дизайн и архитектуру системы так же сильно, как и любые другие требования к архитектуре.
Такой подход к автоматизации позволит использовать меньшие и более экономичные типы автоматизации тестирования, недоступные для систем, созданных без учета автоматизации. Хорошая архитектура предназначена для тестирования, и роль автоматизатора заключается в том, чтобы информировать разработчиков системы об этих требованиях, а также в написании автоматизации после того, как система построена.
Мы должны понимать, что любая автоматизация имеет свою стоимость, и эти затраты создают риск. Так же не стоит забывать, что функциональность должна быть протестирована с помощью множества различных типов автоматических тестов, что задача автоматизации тестирования заключается в правильном использовании этих типов тестов для целостного создания наиболее эффективного и действенного набора средств автоматизации. Ещё раз повторим, что автоматизация так же важна при проектировании системы, как и любое другое требование — это здоровый подход к автоматизации тестирования.
Создание «фабрикой автоматизации» и слепая автоматизация всех тестовых случаев с помощью тестов высокого уровня — нет.
Список литературы:
В интернете есть масса отличных материалов о правильном распределении тестов, типах тестов, инвестициях в автоматизацию и многих других темах, обсуждаемых в этой статье. К сожалению, они погребены в большом количестве воды, спекуляций и маркетинга.
Вот некоторые из моих любимых ресурсов по этим темам:
The Practical Test Pyramid, Ham Vocker
https://martinfowler.com/articles/practical-test-pyramid.html
Testing Strategies for a Microservice Architecture, Toby Clemson
https://www.martinfowler.com/articles/microservice-testing/
The Diverse and Fantastical Shapes of Testing, Martin Fowler
https://martinfowler.com/articles/2021-test-shapes.html
Write Tests. Not too Many. Mostly Integration, Kent C. Dodds
https://kentcdodds.com/blog/write-tests
The Testing Trophy and Test Classifications, Kent C. Dodds
https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications
Testing Pyramid Ice-Cream Cones, Alister Scott
https://watirmelon.blog/testing-pyramids/
Round Earth Test Strategy, James Bach
https://www.satisfice.com/blog/archives/4947
Just Say No to more End-to-End Tests, Mike Wacker
https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html
Testing vs Checking, Michael Bolton and James Bach
http://www.satisfice.com/blog/archives/856
The Regression Death Spiral, Blake Norrish (yes, me)
https://medium.com/slalom-build/the-regression-death-spiral-18f88b9fb030
Test Cases are not Testing, James Bach and Aaron Hodder
https://www.satisfice.com/download/test-cases-are-not-testing
The Oracle Problem in Software Testing, A survey
IEEE TRANSACTIONS ON SOFTWARE ENGINEERING
https://discovery.ucl.ac.uk/id/eprint/1471263/1/06963470.pdf