Tests as code с Allure TestOps и что из этого вышло

Внедрение автоматизированных практик тестирования — очень полезная штука. Однако при подходе к этой задаче возникает масса вопросов. Какую платформу выбрать? Сложной ли будет миграция? Какие подводные камни ждут впереди? В своем посте я расскажу, как мы переносили практику тестирования и внедряли «тесты как код» на базе Allure TestOps.

Достаточно давно (по меркам ИТ-мира) я посмотрел доклад Артема Ерошенко с Heisenbug 2020 «Тест-кейсы как код». С переходом в Леруа Мерлен со старой TMS на Allure TestOps появилось желание полноценно попробовать данный подход у себя. В статье расскажу о том, что из этого получилось.

Привет, Хабр! Меня зовут Сергей Старков, я работаю в области тестирования с 2011 года. Ранее тестировал: десктопные клиент-серверные системы видеонаблюдения вместе с железом для них, кассовое ПО (Windows и DOS) вместе с железом, распределенный монолит, в основе которого Oracle Siebel CRM. В Леруа Мерлен являюсь инженером по тестированию различных продуктов для внутренних и внешних пользователей.

Переезд на новую TMS?  

Не хотелось бы устраивать холивары по выбору TMS-системы. Скажу сразу, что наш приоритет был определен очень прагматично. Allure TestOps по сумме лицензий выходил дешевле своих конкурентов, поддерживать этот продукт проще, а еще он находится полностью в нашей зоне ответственности, также в плюс интуитивно понятный интерфейс и то, что очень просто использовать внешние интеграции с Jira, Jenkins и т. д.
Понятно, что переезд нескольких десятков команд, сотни проектов с тестовой базой, состоящей из десятков тысяч тест-кейсов, пугал, но мы справились — чему сейчас очень рады.

Чего мы ждали от подхода «тесты как код»

IT-составляющая в Леруа Мерлен последние пару лет развивается очень быстрыми темпами. Поэтому нам хотелось сформировать единый подход к написанию тест-кейсов. Кроме того, была надежда снизить трудозатраты на их написание и рефакторинг, а также косвенно увеличить скорость актуализации тестов. Ну и конечно, всё это было сдобрено духом авантюризма: «А что из этого выйдет?»

У нас имеется классический CI/CD, когда по мержу в ветку (например, dev) происходит запуск статического анализа кода приложения, различные проверки информационной безопасности, прогоняются unit-тесты и, наконец, раскатка на соответствующее окружение. После этого запускаются функциональные тесты, и результаты попадают в Allure TestOps. Плюс, при желании, можно запланировать дополнительные уведомления в Slack.

8e62935a37abe6a83dfe0437e9d7d308.png

При миграции из старой TMS для сохранения иерархии папок для тестовых моделей мы решили сделать маппинг с помощью аннотаций @Section… @Section2 и в итоге получили 3 уровня вложенности. Пример аннотации выглядит так:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@LabelAnnotation(name = "section")
public @interface Section {
  String value();
}

В самой TMS был настроен маппинг:

ad55086ca4acb00c0f1c60a4c76f2f3c.png

Затем для удобства фильтрации тестов мы добавили @Layer и настроили необходимый маппинг.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@LabelAnnotation(name = "layer")
public @interface Layer {
  String value();
}

Для удобства отображения ссылок на задачи в Jira добавлены были аннотации @JiraIssues и @JiraIssue, а также маппинг в Issue Schemas.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface JiraIssues {
  JiraIssue [] value();
}
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@LabelAnnotation(name = "jira")
@Repeatable(JiraIssues.class)
public @interface JiraIssue {
  String value();
}

В итоге пример ручного теста получился таким:

@Test()
@AllureId("87996")
@Section("Тесты примеры")
@Section1("API тесты")
@JiraIssues({@JiraIssue("TEST-2")})
@Links({@Link("TEST-2")})_____________________________________________________________
@Tags({@Tag("example")})
@Layer("api")
@DisplayName("Пример API теста")
public void apiTestExample() {
  step("Отправить запрос GET /v1/addresses/store=999&lmCode=10966327", () -> {
  });
  step("Проверить:", () -> {
    step("Получили httpcode=200", () -> {});
    step("В ответе вернулся json:", () -> {
      file.attachFile("Ответ", "examples/example_response.json", "json");
    });
  });
}

После выгрузки результатов в сам Allure TestOps получаем внутри прогона подобный вид теста:

b121473e0adc0d39b2df90985a4e8d53.png

Рабочий цикл тестировщика выглядит теперь следующим образом:

efd33f1f7ecbb331b1c2d9f4301f0095.png

На этапе Create test можно как написать ручной тест-кейс, так и сразу подготовить автотест. Все зависит от конкретной задачи и связанной с ней необходимости.

Сложности, с которыми мы столкнулись

1. Не все поля подлежат маппингу

Как оказалось, аллюр позволяет использовать поля Precondition и Expected result, но данные поля невозможно выгрузить из кода. Принимаем это как ограничение при построении тестов.

7d4d95badd863309fa3e86a31ad81264.png

В коде просто добавляем шаги с описанием предусловий.

step("Предусловия", () -> {
    step("Открыть приложение", () -> {});
  });

2. Ручные тесты под видом автоматизированных

Оказалось, что все выгружаемые тесты (ручные и авто) воспринимаются Allure TestOps как автоматизированные. Из коробки готового примера или аннотации для формализации этого вопроса нет. Поэтому мы написали свою кастомную аннотацию.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@LabelAnnotation(name = "ALLURE_MANUAL")
public @interface Manual {
  boolean value();
}

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

c150f93e9c7957cce21e66a2abc82f67.png

Пока прогон не завершен, такие тесты отображаются со статусом «In progress» (синий цвет). После завершения прогона непройденные тесты получат статус «Unknown» и станут фиолетовыми.

2510de29709653de2bf9b3b3750778fe.png

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

1e8135ea441b162268fce3bbe8734534.png

3. Вложения

Так и не получилось до конца побороть проблему вложений для тестов. Все вложения видны в результатах выполнения, но не отображаются в самом тесте. Это, конечно, не критично. Команда просто привыкла, что необходимо проваливаться в последний запуск теста и уже там смотреть, какой запрос нужно выполнить.

e15cbad9e3ac7aa0c7e20a57d93258e5.png

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

b4680b8915ffe5e798ace209c9210d74.png

4. Версионность тестов

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

Итоги

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

В дополнение теперь при запуске автотестов какой-то группы (smoke, api и т. д.) мы получаем тестовый прогон, включающий все тесты: автоматизированные, устаревшие и ручные. Казалось бы, зачем это в прогоне? Устаревшие тесты маячат перед глазами и сподвигают на их скорейшую актуализацию, ручные сообщают нам о том, что необходимо пройти ручками какую-то часть функционала и отследить динамику автоматизации от прогона к прогону. 

Кроме этого я бы хотел отметить еще пару полезных преимуществ нового подхода:

  • Если вам когда-либо приходилось массово рефакторить пример JSON«а для API-теста, ссылки на макеты или что-то иное, то теперь подобный рефакторинг сводится к простой массовой замене параметров в нужных файлах. И это делается намного быстрее подобных ручных изменений в TMS.

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

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

P.S. А еще мы получили неожиданный эффект: ручные тестировщики начали активно погружаться в автоматизацию. Стоило им познакомиться с IDE, как пропали страхи перед кодингом.

© Habrahabr.ru