Блеск и нищета автоматизации тестирования
Принято считать, что наличие автоматических тестов — это безусловное благо. Если разработчики пишут тесты — это хорошо, чем больше тестов, тем лучше. При этом, в реальности их чаще не пишут, а все тестирование делают вручную.
Не стоит списывать такое положение дел на некомпетентность, глупость или банальную лень разработчиков. По сравнению с ручным тестированием, автоматизированное имеет как достоинства так и явные недостатки. Если бы были одни только плюсы, и говорить было бы не о чем.
Преимущества автоматических тестов
Автоматизированное тестирование имеет ряд существенных преимуществ по сравнению с ручным тестированием.
Дешевизна — прогон автоматических тестов всегда дешевле ручной проверки на несколько порядков. Разработка автоматических тестов конечно дороже разового ручного тестирования.
Неутомимость — автоматические тесты можно запускать снова и снова, у них неограниченный рабочий день, они не устают, и не болеют.
Скорость — автоматические тесты выполняются на порядки быстрее, чем те же проверки, выполненные вручную. Конечно, бывают и сравнительно медленные тесты, но это скорее исключение, подтверждающее правило.
Точность — автоматические тесты проверяют ровно то, что описано в сценарии, во всех деталях и мелочах. Они не отвлекаются, не путают и не забывают.
Всеобъемлемость — автоматические тесты позволяют покрыть огромное количество сценариев и находить наведенные ошибки в таких далеких углах приложения, до которых ручной тестировщик никогда бы не добрался, кроме как случайно.
Преимущества ручного тестирования
Ручное тестирование, тем не менее, превосходит автоматизированное по многим аспектам.
Проворность — проверить что-то вручную в первый раз легко и быстро. Автоматический тест нужно прежде разработать. Практически всегда это гораздо медленнее ручной проверки.
Гибкость — ручное тестирование можно проводить гораздо разнообразнее, и изменение способа тестирования практически ничего не стоит. Давайте протестируем в Safari — пожалуйста, на Chromebook — нет проблем, IE6 — придется запустить виртуальную машину, но тоже можно.
Адаптируемость — умение быстро подстраиваться под изменения. Когда продукт серьезно меняется, и все начинает работать совсем не так, как раньше, ручные тестировщики легко могут забыть, как они тестировали прежде. Они просто будут тестировать то, что есть сейчас. Автоматические тесты в той же ситуации выдадут тысячи ошибок, и их придется исправить, прежде чем двигаться дальше.
Креативность — ручное тестирование позволяет найти проблемы, которые заранее не были известны, более того, даже не подразумевались.
Осмысленность — хотя каждая вещь в отдельности может быть абсолютно корректна, люди гораздо легче понимают, что вместе эти вещи не имеют никакого смысла.
Выразительность — мало найти проблему на уровне «ничего не работает!», важно правильно объяснить, в чем она заключается и уметь ответить на дополнительные вопросы разработчика. И в этом люди также лучше автоматических тестов.
Аутентичность — большинство приложений мы пишем для людей, и именно поэтому люди лучше всего подходят для их тестирования.
Блеск парадоксов тестирования
Говоря о тестировании, и тем более о полном тестировании, мы немного лукавим. Очевидно что проверить все невозможно.
Все что мы можем сделать на практике, это осуществить некоторое достаточное тестирование. В результате мы с определенной долей вероятности сможем утверждать что большинство пользователей не столкнутся с проблемами.
Потратив на тестирование 1 день, мы добьемся 90% довольных пользователей. Тестируя неделю — 99%, тестируя месяц 99.5%. На достижение все меньшего и меньшего результата мы тратим все больше времени. Это нецелесообразно.
Исправляя проблемы быстро, можно добиться гораздо более высокого качества продукта, нежели вкладывая все имеющиеся ресурсы в достижение полного покрытия тестами. Соль в том чтобы найти баланс в приложении сил.
Ручное тестирование дает гораздо больше отдачи, если разрабатывать нужно быстро, особенно если приложение меняется инкрементально, в локальных предсказуемых частях. Причина в том, что проверить изменение руками гораздо быстрее, чем автоматизировать тот же тест.
Автоматическое тестирование дает очень маленькую отдачу на коротких дистанциях. В каждый отдельный момент, решая что выгоднее, писать тесты или не писать, второе всегда предпочтительнее.
Ситуация в корне меняется в длительной перспективе. Код приложения необходимо рефакторить, чтобы он не умер. Когда программисты все переделали — тестировщики должны все проверить. Полный цикл регрессионных тестов. Неделя? Две? Месяц? Или еще хуже: программисты обновили версию ключевой библиотеки. День работы, вроде все компилируется — отлично! И все равно тестировщики должны проверить все. Безумие. Пройдя через такое несколько раз, любой будет готов пойти на что угодно, лишь бы избежать подобных ситуаций в будущем. Для обеспечения стабильного базового уровня качества необходимо вкладываться в автоматизацию тестирования.
К сожалению, автоматические тесты не всегда являются решением, иногда они сами становятся проблемой.
Нищета автоматизации тестирования
Проблема: нужно уметь проверять приложение полностью и быстро, ничего не упуская, много раз подряд.
Решение: написать автоматические тесты, которые все это делают.
Но не все так просто.
Написание тестов — это разработка, точно такая же, как и разработка основной бизнес-логики. Она требует ресурсов и квалификации. Обеспечивая качественное покрытие кода приложения тестами мы одновременно замедляем разработку как таковую. И замедляем не на 10%, а вдвое, втрое или даже больше.
Скорость — понятие относительное. Каждый конкретный тест, конечно, выполняется очень быстро, особенно чистые unit-тесты, когда все проверки происходят локально. Интеграционные тесты, REST-тесты тоже сравнительно быстрые. Что такое 100 миллисекунд, ну пусть даже 1 секунда, даже десятки секунд для selenium тестов — ерунда по сравнению с ручным тестированием. Но когда количество тестов хоть сколько-нибудь существенно, быстрые тесты оказываются очень даже медленными. Прогон тестов за 5 минут? Полчаса? Три часа? Два дня? Еще даже не добившись полноценного покрытия бизнес-логики, всерьез встает вопрос о том как запускать не все тесты, или запускать тесты не каждый раз — иначе уже автоматические тесты начинают тормозить разработку.
Примечание: Тесты можно существенно ускорить, вложившись в железо — выделить под них отдельный сервер, 10 серверов, 50 и т. д. Некоторые компании могут позволить себе и 1000 тестовых серверов, но далеко не все.
Инструментарий плох. Ни одна из мейнстримных платформ программирования не проектировалась с целью обеспечить возможность максимально просто и полноценно разрабатывать тесты. В Java все тестовые фреймворки являются сторонними библиотеками. Запуск тестов, осуществление проверок, создание mock-объектов — все это есть, но по частям и вне платформы. В Go тестовый фреймворк уже добавили в экосистему языка изначально, но это только запуск тестов. Выбор подходящего инструментария для написания тестов это отдельная проблема, требующая решения.
Не такие уж они и хорошие. Тесты — такой же код, написанный такими же разработчиками. Они тоже содержат ошибки и все те же проблемы, что и любой другой код. Бывает, что тест ничего не проверяет. Бывает, что тесты тоже нужно рефакторить. Бывает, что по упавшему тесту очень трудно понять, в чем проблема. Бывает, что тесты падают просто так, когда ошибки на самом деле нет.
Блокируют изменения. По своей природе тесты призваны проверять, что ничего не изменилось. Если что-то начало работать не так — это баг. Но когда бизнес-логика меняется, даже если изменение корректно и полностью соответствует новым требованиям — тесты об этом ничего не знают. Изменений нескольких правил может привести к необходимости исправить десятки, если не сотни тестов, особенно если покрытие действительно хорошее.
Ограничивают рефакторинг. Тесты тоже нужно рефакторить, даже если бизнес-логика не меняется, и в коде происходят лишь косметические изменения. Это может привести к тому что нужно поправить 5 мест в основном коде и еще 80 в тестах. Каждый в такой ситуации задумается:, а стоит ли делать что-то большее, чем переименование метода.
Сизифов труд — вот во что превращается разработка через тестирование, когда тесты пишутся до написания кода или хотя бы по ходу дела. Любой грамотный разработчик рефакторит код в процессе его написания по мере того, как он все глубже разбирается в проблеме и понимает, как реализовать решение лучшим образом. Но если он пишет тесты одновременно с кодом, кроме изменений в коде ему приходится менять еще и тесты. А в отличии от рефакторинга готового кода, рефакторинг кода в работе обычно гораздо масштабнее, значит и требуемые изменения в тестах будут соответствующими. И так — много раз еще до того, как код уйдет на ревью. Половину рабочего времени в мусор — легко.
Количество тестов обманчиво: 3000 тестов в сумме — на веб-интерфейс, REST API, бизнес-логику и юнит-тесты — могут проверять с натяжкой 1000 ситуаций. Все остальное — дублирование.
Автоматическая регрессия не отменяет ручную вопреки главной задумке. Все потому что автоматические тесты проверяют много чего, но далеко не все. Но что конкретно они проверяют, а что нет — никто не знает. Нам нужно проверить регистрацию. А еще у нас есть 18 927 тестов. Если какой-то из них красный — «все хорошо», можно вернуть задачу к разработчикам — пусть разбираются. Если все тесты зеленые — это ничего не значит до тех пор, пока человек, отвечающий за ручное тестирование, не будет уверен, что из логики регистрации проверяется автоматически, а что все еще нужно проверить вручную. А тестов почти двадцать тысяч — в этом невозможно разобраться. Результат — проверить вручную нужно все.
Неудивительно что некоторые уходят из разработки продавать пластиковые окна, обучать бильярду или варить кофе (и это все реальные случаи).
О том какие выводы мы в Wrike сделали из собственных и чужих ошибок, расскажу в следующем посте.
Дмитрий Мамонов
Департамент разработки,
Подразделение мержа в мастер,
Отдел работы с гит,
Ведущий оператор баш консоли 1 разряда