Введение в автоматизацию ПО

18-11-2019

Важность автоматизации (тестов)

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

Переход от ручного к автоматизированному тестированию

Нет причин не автоматизировать любую повторяющуюся задачу. Но до сих пор при разработке ПО присутствует большая доля ручного тестирования, которое очевидно является и медленным и не таким "эффективным", как автоматизированное. Первая мысль, посещающая любого, кто желает улучшить процесс тестирования - это автоматизировать ручные задачи (или задачи ручного тестировщика). Самое простое решение для этого - "записать" действия ручного тестировщика и затем "проигрывать" их раз за разом (например, при выполнении регрессионного тестирования). Записать действия пользователя можно на уровне UI (наиболее частая практика), либо на уровне запросов к серверу (например используя Postman, Jmeter) или на уровне записи любых данных, которые неявно отражают активность пользователя. Даже если Вы сможете автоматизировать абсолютно все проверки, проводимые ручным тестировщиком, всё равно найти ошибки (баги) в автоматическм режиме довольно непростая задача. Одним из наивных подходов может быть сравнение в автоматическом режиме картинки (скриншота) экрана с ожидаемой картинокй, которая была записана при создании теста. У этого подхода есть много недостатков (в основном технического плана). Например, можно столкнуться с тем, что качество изображения будет зависеть от разрешения экрана, в данных, которые могут появлятся на экране и т.д. Главная проблема этого подхода в том, что любое изменение приложения будет восприниматься как ошибка (упавший тест), делая трудным анализ результата - ведь причиной упавшего теста может быть как реальная ошибка, так и ложное срабатывание (false positives).

Получите максимум от автоматизации тестирования

Посмотрим на автоматизацию с точки зрения идеального ожидаемого результата - что в идеале мы бы хотели получить от автоматизации? Это противоположный подход традиционного желания автоматизировать только те тесты (ручные), которые мы уже имеем на данный момент. Итак представьте себе Рай:

  • у Вас имеется полное покрытие автоматическими регрессионными тестами, которые все вместе запускаются за нескольок минут,
  • Ваша команда исправила ВСЕ известные ошибки (баги) и все Ваши тесты "зелёненькие",
  • каждый разработчик может запускать эти тесты на своём окружении (когда пожелает) ...

Как бы не было трудно Вам всё-это представить :-) - теперь нужно представить себе еще одно - А как Вы теперь будете использовать автоматизацию? Запускали бы Вы тесты только ночью и анализировали бы тесты утром?... найдя баг, описали бы Вы его в бактрекинговой системе и ожидали бы его исправления? ... Конечно же - НЕТ! и НЕТ! Если тесты быстры и их запуск не требует много времени, тогда Вы могли бы запускать их перед каждой операцией check-in от каждого разработчика и блокировать check-in операцию если 1 или более тестов упали. Это бы гарантировало, что всё что лежит внутри системы хранения кода всегда проходит наши тесты! Это было бы состояние с нулевым количеством известных багов. Именно эта идея лежит в основе концепции Continuous Integration (CI, непрерывная интеграция). Кроме этого, такой подход вдохновлял бы разработчиков на рефакторинг кода (для улучшения его внутреннего качества), он позволял бы безболезненно рефакторить ибо гарантировал бы то, что рефакторинг ничего не сломал. Это внутреннее качество также распространяется и на внешнее качество кода, ведь чем проще код - тем больше тенденция к содержанию меньшего количества багов и такой код легче поддерживать, не создавая новых багов. В добавок, если новый фукционал будет добавлен и связанные с ним новые тесты + старые тесты прошли и нет новых багов, то это новое свойство будет рассматриваться командой разработки как "реализованное". Мы описали идеальный случай, которого так и не удалось достичь большинству команд разработки, однако, если Вы будете внедрять описанный подход с первого дня, то он окажется реальным и выполнимым.

Разница между ручными и автоматическими тестами

Теперь мы видим, что успешная реализация автоматизации тестирования имеет некоторые большие преимущества, которые Вы не можете получить от регрессионного тестирования вручную. Различия между ручными и автотестами особенно важно учитывать при переходе к реализации существующих планов ручного тестирования в качестве автоматизированных тестов. В общем случае, выполнение ручных тестов разделяется на 2 категории:

  • Exploratory testing (исследовательское тестирование)
  • Planned testing (спланнированное тестирование)

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

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

С другой стороны мы конечно можем использовать в автотестах разные граничные условия. разные входные данные, что позволит нам расширить спектр и скорость проверок, однако по мере усложнения проверок и их логики нам станет тяжелее поддерживать автотесты. Хорошей рекомендацией в этом вопросе является использование простых тестов, выполняющих только одну или немного специфических проверок. Этот метод называется property-based testing, и наиболее известным инструментом для этого является QuickCheck, который был изначально создан в языке программирования Haskel, но позже был перенесен в длинный список других популярных языков, включая Java, F #, Python, Ruby, JavaScript, C / C ++ и т.д.

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

На сегодняшний момент для автоматизации исследовательского тестирования автоматизация может только предложить что-то на подобие Monkey testing :-) . MONKEY TESTING – термин, описывающий практику случайного нажатия клавиш (или другими словами говоря, выполнения операций, не понимая их назначение), как обезьяны или, малыши, мы можем с помощью этой "техники" посмотреть, работает ли программа или нет. Хотя этот метод может быть легко автоматизирован, он обычно не очень эффективен по нескольким причинам:

  • Вы можете ловить только сбои (или ошибки, которые Вы явно ищете), а не любые, ибо Вы не сможете определить ожидаемый результат для каждого действия. Даже если программа зависает (но не падает), Вы, вероятно, не сможете обнаружить это, не говоря уже о том, чтобы определить, ведет ли себя приложение ожидаемо.
  • так как автоматический тест будет нажимать клавиатуру (мышь) вслепую (случайным образом), то шанс выполнить что-то полезное и интересное очень невелик. Например, как такая автоматическая обезьяна закроет диалоговое окно или правильно заполнит форму? Скорее всего она будет часами зависать на таких Ui интерфейсах, делая запуск таких "автотестов" бесполезным а даже вредным :-) (с точки зрения разумного использования ресурсов человека и компьютера).

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

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

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

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

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

Должны ли мы перезапускать упавшие тесты?

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

Размер тестов

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

Независимость тестов

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

Должен ли тестовый фреймворк автоматически "оформлять" баги?

Наверное, это не очень хорошая практика ибо:

  • часто причиной падения теста не явлется баг в тестируемой системе
  • часто несколько тестов падают из-за одинаковой причины

Ожидания и реальность хорошо спроектированных автотестов

Ожидание Реальность
Причиной падения автотестов является "встреча" с реальными багами В основном, причиной падения автотестов является "встреча" с реальными багами. Но по результатам запуска автотестов мы с лёгкостью можем исследовать и установить реальную причину падения тестов.
Автотесты не пропускают баги Автотесты не пропускают только баги, для поиска которых они были спроектированы

А вот о том, как достичь такой "реальности", будем разбираться в других статьях.

Литература

  1. "Complete Guide to Test Automation": Techniques, Practices, and Patterns for Building and Maintaining Effective Software Projects. Arnon Axelrod - 2018