Любой проект в процессе своего развития и роста наполняется новыми функциональными возможностями. QA-процессы должны на это оперативно и адекватно реагировать, например, увеличением количества тестов всех видов. В этом докладе мы будем говорить про UI-тесты, которые играют важную роль в создании качественного продукта. Система автоматизации UI-тестирования не только в разы сокращает время на регрессионное тестирование, но и обеспечивает эффективную работу таких инструментов и процессов разработки, как Continuous Integration и релиз-инжиниринг.

Количество тестов постепенно растет от 1000 к 3000, от 6000 к 9000+ и т.д., и, чтобы эта «лавина» не накрыла наш QA-процесс, нужно с самого раннего этапа развития проекта автоматизации думать про эффективность всей системы и каждого теста в ней.

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

План доклада:


  1. Начнем с принципов «тестостроения», которые делают нашу систему максимально удобной для использования;
  2. Разберем способы интеграции системы UI-тестирования в процессы QA-команды;
  3. Посмотрим на конкретные приемы улучшения эффективности каждого теста;
  4. Поговорим про метрики системы UI-тестирования и про её взаимоотношения с проектами Continuous Integration и релиз-инжиниринга;

Требования к системе UI-тестирования и принципы «тестостроения»


К системе автоматизации UI-тестирования мы предъявляем следующие требования: системой должно быть легко пользоваться, она должна быть интуитивно понятна, поддержка тестового покрытия не должна отнимать много времени, система должна быть устойчива к ошибкам в коде тестов и, наконец, система должна быть очень производительна.

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

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

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

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

Используйте pre-push и post-commit хуки для защиты работоспособности «ядра» вашей системы. Как минимум, запускайте в них юнит-тесты.

Юнит-тесты — это тесты, которые позволяют проверить на корректность отдельные модули исходного кода программы. Важно не путать юнит-тесты и UI-тесты — они не взаимозаменяемы, они дополняют друг друга.

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

UI-тестирование в процессах QA-команды


Как мы проинтегрировали систему UI-тестирования в команды и в продуктовые процессы. Главная цель — любые тесты должны приносить пользу с самого раннего этапа разработки. Как только тест написан, он сразу же попадает в систему Continuous Integration. Поддержка тестового покрытия должна быть стандартной частью тестирования задачи. Поэтому в Туту.ру нет тестировщиков, которые занимаются только ручным тестированием. У нас каждый специалист занимается полным спектром тестирования.

Задача не должна «мерджиться» в master, если она ломает какие-либо тесты. Постоянно держим это в голове, даже если заказчик торопит.

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

Детализация релизного цикла
Детализация релизного цикла одной из команд в человеко-часах

Основные этапы релизного цикла
Трудозатраты на основные этапы релизного цикла одной из команд в человеко-часах

UI-тесты в релизном цикле


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

Любая разработка начинается с этапа «story branch». На этом этапе мы запускаем комплект тестов, который формируется тестировщиками. Запускать его может кто угодно — разработчик, тестировщик или аналитик. Проводится актуализация тестового покрытия, и в этом участвует ответственный за тестирование данной задачи сотрудник QA-отдела.

Следующий этап — Pre-rc. Это «ночные сборки». Специальная ветка собирается на тестовом стенде каждый день. На ней происходит прогон всего пула тестов, которых у нас больше 9000. Каждая команда использует результаты этой работы. На этом этапе происходит финальная доработка тестового покрытия.

Следующий шаг — RC. Это наш релизный процесс, релизимся мы два раза в неделю. Используется для этого специальный релизный комплект тестов, и на этом шаге тесты должны быть практически все «зеленые», если что-то не так, это исправляется.

Финал — релиз (stable). На релизе также используется релизный комплект тестов.

Поддержка проекта


Отдельная роль — саппортер проекта, для оперативного решения проблем QA-команды. Для команды очень важна постоянная поддержка работоспособности инструмента. Мы пользуемся Service Desk для того, чтобы каждый сотрудник мог получить поддержку при использовании системы.

Улучшаем эффективность каждого теста


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

Проект контроля и управления тестовыми контейнерами


Тестов много. Система мультипоточного запуска состоит из более чем 150 тестовых контейнеров, за которыми нужно следить. Мы сделали инструмент, который позволяет управлять тестовыми потоками, предоставляет информацию о загруженности и дает наилучшую интеграцию с модулем Runner.

Servers_queue
Интерфейс системы управления контейнерами для UI-тестов

Свой Runner тестов


Мы написали Runner UI-тестов. Для нас первостепенным было низкое потребление ресурсов. Нам важна гибкость в развитии — нужно отвечать на требования бизнеса. Runner умеет балансировать загруженность своей системы с учетом приоритетов запусков. Запуски релизного цикла и циклов devel-окружения имеют разные приоритеты. Runner балансирует их, учитывая эти условия. А еще он обеспечивает лучшую интеграцию с другими модулями системы.

Внутреннее устройство


Специальный php-скрипт формирует из репозитария очередь тестов. Она попадает в наш модуль мультипоточного запуска, где он fork’ается на отдельные тесты (PHP-процессы). Каждый такой отдельный процесс имеет доступ к базе данных, где получает данные пользователя, под которым тест будет выполнен.

Core
Схематичное устройство модуля Runner

Тест-кейсы и управление тестовыми комплектами


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

Для уже покрытых тест-кейсов используется тег case:

case

Для тест-кейсов, которые еще не реализованы в коде, — тег todocase:

todocase

Для тест-кейсов, которые нужно проводить только вручную, — тег manualcase:

manualcase

Расчет тестового покрытия


Также, при помощи механизма тегов мы автоматически считаем UI-покрытие каждого из проектов. Расчет по формуле:
Процент_покрытия = (1 — (количество_todocase-тестов + количество_manual-тестов) / общее_количество_тестов_в_проекте) * 100%

coverage
Вывод консоли с процентом покрытия UI-тестов для проекта «Автобусы»

Управление тестовыми комплектами


Тот же самый механизм мы используем для того, чтобы управлять тестовыми комплектами. К примеру, мы используем тестовые наборы для определенной функциональности, есть наборы для релизного, RC-циклов, и вообще, создание наборов ограничивается только фантазией QA-специалистов. Каждый тест может быть включен в несколько наборов, мы их обозначаем при помощи тега @labels.

labels
Тест относится к релизному комплекту тестов

runByLabels
Пример запуска комплекта тестов на функциональность success-страницы

HTML-репорт


Репорт формируется индивидуально для каждого запуска. Каждый запуск тестировщик может сделать руками и получить в виде отчета HTML-страницу, тем самым QA-специалист получает возможность быстрой оценки качества продукта. HTML-репорт уменьшает время актуализации тестов. Отчеты есть в инструменте CI, но тестировщику иногда полезно увидеть отчет именно в своей рабочей копии.

«Soft asserts»


PHPUnit, как и любая другая система unit-тестирования с ассертами работает так: если ассерт сталкивается с несовпадением данных, то выполнение теста не продолжается. Мы изменили эту парадигму. «Мягкие» ассерты, как мы их называем, когда сталкиваются с проблемой, не прерывают выполнение теста, а продолжают его выполнение со всеми другими ассертами, и, в конечном счете, тест завершает свое выполнение ошибкой на teardown-этапе. Таким образом, мягкие ассерты позволяют дать информацию о качестве целого блока в рамках одного теста, даже если в данном блоке есть какие-то проблемы. Такие ассерты более безопасны в сложной тестовой логике. Для примера: у нас есть тест, который делает заказ с банковской карты на реальные деньги, и мы не хотим, чтобы этот тест «упал» где-нибудь после оформления заказа и не успел его отменить.

Гибкая система настроек запуска


Она была создана, чтобы отвечать потребностям QA-специалистов, а именно — обеспечивать наилучшую интеграцию с CI. Написана она при помощи Symfony Console и на данный момент имеет более 30 параметров.

Несколько из них:
On-demand. У нас есть тесты, которые имеют высокую рискованность и не запускаются автоматически. Они запускаются, если тестировщик указывает в параметрах запуска «on-demand».
Bug-skipped-тесты. Тесты, которые заблокированы какой-либо проблемой в продукте, можно пометить специальным лейблом, и эти тесты не будут запускаться в системе CI. Чтобы поймать ситуацию, что продукт исправил что-то, и тест уже можно обратно выключать, у нас есть специальный план, который запускается раз в неделю, и он запускает только те тесты, которые сейчас деактивированы.
Js-error-seeker. Этот тест особенно полезен для фронтендеров. Фронтендеры и тестировщики используют эту возможность для того, чтобы во время прохождения теста делать проверки на js-ошибки. При помощи данного механизма мы можем отловить js-ошибку на всем пути работы теста.
Notify-maintainers. У каждого теста может быть майнтейнер. Это сотрудник, который несет ответственность за этот тест и хочет получить уведомление, если данный тест упадет. Вышеуказанный флаг включает это уведомление.

Метрики


Проект контроля и управления тестовыми контейнерами умеет мониторить загруженность системы, тем самым показывая точки роста системы.
Графики прохождения тестов на разных стендах по времени. У нас установлен лимит в 60 минут, если какой-либо проект выходит за это время, то это повод пойти в проект и разбираться, почему комплект тестов так долго проходит.

testsTime
Время прохождения тестов по проектам на Production

Релиз-инжиниринг


Здесь мы поговорим про то, как инструмент UI-тестирования должен взаимодействовать с проектом релиз-инжиниринга.

Что умеет связка системы UI-тестирования и Bamboo: запускать билды, формировать отчетность, поддерживать релизный процесс, автоматически запускать билды по расписанию. Также есть возможность автоматического «отката» релиза, если smoke-набор тестов показал, что что-то пошло не так.

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

Выводы


  1. Каждый тест должен запускаться как можно чаще;
  2. Взаимная интеграция модулей системы UI-тестирования очень важна, ради этого стоит писать свою реализацию;
  3. Система должна быть гибкой и поддерживаемой, готовые инструменты не всегда отвечают этим запросам;
  4. Следите за показателями QA-процессов и улучшайте их, как только видите, что что-то идет не так;
  5. QA должно быть частью процесса разработки, наши инструменты должны это поддерживать.

Комментарии (12)


  1. dimkss
    16.10.2017 11:04

    В рассчете процента покрытия тестов хорошо бы добавить еще одни скобки?:
    Процент_покрытия = 100 — (((количество_todocase-тестов + количество_manual-тестов) / общее_количество_тестов_в_проекте) * 100).


    1. Sakharov Автор
      16.10.2017 11:42

      dimkss Спасибо за совет! Переписал формулу, сделал её в целом более читаемой.


  1. nizkopal
    16.10.2017 19:56

    Интересная статья, спасибо.

    Несколько вопросов по поводу показателя покрытия UI тестов:
    1) Насколько вы ему доверяете? Откуда берется понимание, что действительно все кейзы так или иначе описаны в тестах?
    2) Не думали ли вы про приоритезацию кейзов? Чисто интуитивно кажется, что todo на тест покупки должен «бить» по покрытию больше, чем todo теста на клик какой-нибудь мало популярной ссылки в футере.
    3) Этот показатель как-то влияет на ваши процессы? Если он падает, о чем это вам говорит? Есть ли у него какой-то минимум, после которого весь отдел автоматизации дружно выбрасывается в окно начинает работать по ночам?


    1. Sakharov Автор
      17.10.2017 11:37

      nizkopal, спасибо!
      1. «Насколько вы ему доверяете? Откуда берется понимание, что действительно все кейзы так или иначе описаны в тестах?»
      Доверяем мы ему настолько, что на данный момент это наш единственный способ подсчета метрики покрытия UI-тестами. Про понимание того, что все кейсы описаны в тестах: Понимание основано на том, что тесты и описание к ним хранятся в одном месте, т.е у нас просто нет дублирующей системы хранения тест-кейсов, если QA-специалисту нужно добавить в систему регрессионный тест, то он так или иначе идет в код и в нем может сразу создать тест или написать описание с минимальной «заготовкой» под тест. И замечу, что у нас нет цели добавить в систему все кейсы, мы очень гибко подходим к каждому проекту и формируем для проектов именно то покрытие, которое им необходимо, не тратя лишние ресурсы.

      2. «Не думали ли вы про приоритезацию кейзов? Чисто интуитивно кажется, что todo на тест покупки должен «бить» по покрытию больше, чем todo теста на клик какой-нибудь мало популярной ссылки в футере.»
      При постановке задач на расширение покрытия приоритет todocase, конечно, играет роль, QA-специалист в большинстве случаев сам может принять решение по приоритетам, поэтому чаще всего приоритеты мы в описание не пишем, но делать это можно.

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


      1. nizkopal
        17.10.2017 15:04

        Понял, спасибо! Вы — молодцы. :)


  1. samizdam
    17.10.2017 07:34

    Спасибо за обстоятельную публикацию. Есть два вопроса.


    Время прохождения тестов по проектам на Production
    Вы тесты на проде выполняете? Зачем?

    И второй: Насколько я понял вы написали собственную реализацию приёмочных UI-тестов поверх webdriver для phpunit? И не совсем понятно тогда, что за отчёт генерится, о каком покрытии здесь речь...


    1. Sakharov Автор
      17.10.2017 12:06

      samizdam, спасибо!
      1. «Вы тесты на проде выполняете? Зачем?»
      Да, мы выполняем специальный комплект тестов на Production. В первую очередь это нужно для того, чтобы гарантированно предоставлять нашим клиентам высокий уровень сервиса. Production-окружение имеет ряд отличий от наших stage-систем, как на «железном» уровне, так и на уровне контента, поэтому выполнение тестов полностью оправдано и нас отдельно радует то, что тесты на Production практически всегда так, как мы и ожидаем — это результат большой работы.

      2. «Насколько я понял вы написали собственную реализацию приёмочных UI-тестов поверх webdriver для phpunit? И не совсем понятно тогда, что за отчёт генерится, о каком покрытии здесь речь…»
      В основе нашей системы лежит PHPUnit, при помощи него осуществляется непосредственный запуск каждого теста. В статье не идет речь про компонент PHP_CodeCoverage, мы формируем отчет о покрытии проекта UI-тестами и строим его на основе тех данных, которые несут сами тесты в своем описании при помощи специальных лейблов.


      1. Sakharov Автор
        17.10.2017 12:17

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


        1. samizdam
          17.10.2017 23:05

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


          Если для метрики покрытия используется кол-во описанных, но не реализованных тест кейсов, то как избежать следующей ситуации: Функциональность добавилась, тесты нет => покрытие осталось прежним…
          Или я опять недопонял алгоритм рассчёта…


          1. Sakharov Автор
            18.10.2017 00:15

            Если для метрики покрытия используется кол-во описанных, но не реализованных тест кейсов, то как избежать следующей ситуации: Функциональность добавилась, тесты нет => покрытие осталось прежним…

            Тут вы абсолютно правы, если функциональность добавилась и QA-специалист не добавил в систему todocase или не написал тест, то покрытие останется прежним, по-другому можно сказать, что данная функциональность не была добавлена в регрессионные проверки.
            Во первых хочу отметить, что действительно не каждая функциональность должна добавляться в регрессию, это зависит от многих факторов, а во вторых, в этом вопросе мы доверяем тестировщикам, они лучше всех знают когда в систему UI-тестирования нужно добавить тест или «заявку на покрытие» — todocase. Наша система, как и любая другая требует работы по актуализации :)


            1. samizdam
              18.10.2017 23:37

              Понятно, спасибо.

              Вы смотрели в сторону Codeception / Gherkin? Чем эти инструменты не угодили?

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

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


              1. Sakharov Автор
                19.10.2017 09:40

                Codeception пробовали/использовали — отказались по причине внутреннего для него meta-языка (например, $I->see('Home')), который нам совсем не подходит. Из-за этого пришлось писать обвязку над Codeception, которая дает возможность писать на «чистом» PHP, что в совокупности, архитектурно не очень хорошее решение.

                Gherkin — не про нас, тоже навязывает свой мета-язык, который нам не близок.