Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

STEPS-архитектура на примере horizon-тестов openstack'a.

design-patterns
step-object
framework
python
architecture
strategy
webdriver
pytest
Теги: #<Tag:0x00007f7b652b89e0> #<Tag:0x00007f7b652b88a0> #<Tag:0x00007f7b652b8760> #<Tag:0x00007f7b652b8580> #<Tag:0x00007f7b652b8440> #<Tag:0x00007f7b652b8300> #<Tag:0x00007f7b652b81c0> #<Tag:0x00007f7b652b8080>

(Sergei Chipiga) #1

Всем привет,

Недавно я делился кодом #webdriver -тестов с параллельным запуском и видеозахватом: Параллельный запуск selenium-тестов с pytest в нескольких xvfb с видеозахватом каждого на локальной машине (без selenium-grida). Здесь хотел бы подробнее рассказать про примененную в том проекте #architecture и методику построения автотестов (далее, STEPS).

#История вопроса:

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

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

На прошлой работе меня учили, что правильная концепция :triangular_flag_on_post:“один тест - один ассерт”. На мой взгляд, это хорошо для простых автотестов уровня unit и примитивных интеграционных. В сложных end2end сценариях, насыщенных функциональным взаимодействием, этот подход плох, потому что ассерт в конце теста может быть зайфелен по причине того, что где-то за 5 или 10 шагов до что-то пошло не так. При этом руткоз выяснить довольно трудно, тем более в сложных приложениях куда вероятнее наткнуться на плавающий баг, чем на воспроизводимый.

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

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

Введение стэпов:

Суть STEPS проста: тест рассматривается как последовательность шагов (что не есть секрет), при этом каждый шаг является атомарным и верифицируемым. Каждый шаг делает минимальное законченное действие после чего верифицирует, что результат соответствует ожиданиям. Это дает возможность уронить тест именно в том месте, где произошла ошибка. Что будет минимальным действием - зависит от вида тестирования, но в целом это все то, что относится к CRUD операциям над ресурсами. Можно выделить следующие аксиомы в e2e тестировании:

  • Тест - ничто иное как последовательность действий над ресурсами.
  • Ресурс - это объект или структура данных.
  • Тест может быть разбит на последовательность стэпов.
  • Можно выделить 3 типа стэпов:
    • get-стэп для извлечения ресурса(ов)
    • change-стэп для изменения ресурса(ов)
    • check-стэп для верификации ресурса(ов)
  • Get- и change-стэпы содержат верификацию результата стэпа и могут включать check-стэпы.
  • Верификация может быть отключена н-р для проведения негативных тестов.
  • В целом выгоднее в стэпе совершать действия над коллекцией ресурсов, рассматривая единичный случай, как множество с одним элементом.

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

Реализация в коде:

В STEPS архитектура кода состоит из следующих уровней:

  • Клиенты для управления ресурсами.
  • Стэпы используют клиенты, чтобы совершать действия над ресурсами и верифицировать результат.
  • Фикстуры используют стэпы для ресурс-менеджмента.
  • Тесты комбинируют стэпы в соответствии со сценарием.

Фикстуры и тесты стоит рассматривать в контексте #pytest’a, поскольку этот #framework был заслуженно выбран для конечной реализации автотестов.

Стоит сказать, что STEPS не ограничивает разработчика в том, ЧТО нужно написать. Но ограничивает в том, КАК это написать. Есть всего два ограничения:

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

В частности в UI-тестах horizon’a это позволило отделить описание #page-object model от совершаемых действий. Код описания страницы - декларативный: для страницы регистрируются значимые UI-элементы в соответствии с их иерархией, н-р: https://github.com/Mirantis/mos-horizon/blob/v9.1/horizon_autotests/app/pages/containers.py.

Действия же над элементами осуществляются в стэпах, н-р: https://github.com/Mirantis/mos-horizon/blob/v9.1/horizon_autotests/steps/containers.py.

Для декларирования структуры страницы был разработан микрофреймворк POM (https://github.com/sergeychipiga/pom), задача которого - скрыть низкоуровневую логику вызовов selenium’a (можно подробнее рассмотреть в отдельной статье), и предоставлять наружу высокоуровневый #api для управления UI-элементами.

Дальнейшее развитие:

Базовые знания по имплементации стэпов, полученные при разработке автотестов horizon’a, получили дальнейшее применение в новом активно развиваемом проекте по автоматизации тестирования Openstack’a - stepler (http://stepler.readthedocs.io, https://github.com/mirantis/stepler). При этом тесты horizon’a в нем уже являются лишь одним из многочисленных компонентов. И взаимодействие компонентов организовано по SOA-принципу, когда фикстуры одного компонента выступают для другого в качесте сервисов. В целом это позволяет строить децентрализованную #architecture , и тесты на дополнительные компоненты могут находиться в разных репозиториях, подключая stepler одной строкой импорта требуемых фикстур. Кроме того, это также позволит тестировать сборки openstack’a, включающие различные комбинации компонентов, запуская только те тесты, которые не зависят от отсутствующих компонентов.

Если будет интерес по архитектуре stepler’a, можно будет рассмотреть это в отдельной статье, н-р там используется #strategy и рекомендации для избежания конфликта в порядке фикстур и финализации ресурсов, callable-фикстуры, context-фикстуры, и многое другое, с чем приходится сталкиваться в процессе разработки.


Параллельный запуск selenium-тестов с pytest в нескольких xvfb с видеозахватом каждого на локальной машине (без selenium-grida)
(Mykhailo Poliarush) #2

Спасибо большое за статью :clap: , если будет время, буду рад увидеть еще и статью в #baza-znanij на POM микрофреймворку с объяснениями.


(Sergei Chipiga) #3

Да, окей, думаю в ближайшее время накидаю статейку про POM