Написание автотестов для сервиса миграции

от автора

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

Стояла задача автоматизации регресса для сервиса миграции из системы Jira во внутреннюю систему «Яга».

Если упростить, то типичный тест-кейс представлял из себя следующие шаги:

  1. Создание тестового проекта в Jira

  2. Обогащение проекта данными

  3. Миграция тестового проекта в «Яга»

  4. Проверка успешности миграции

Давайте детальнее разберем реализацию каждого пункта.

1. Создание тестового проекта в Jira

Первая проблема, которую я так и не смог до конца побороть — это создание проекта в Jira.

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

В облачной версии большая часть этих сложностей уже устранена, но увы, у нас серверный вариант.

В сложившейся ситуации наименее затратным решением оказалось создать шаблон проекта, используя недокументированный endpoint shared configuration. Далее на основании этого шаблона с уже описанным бизнес процессом можно легко создавать другие проекты.

Минусом такого решения является зависимость тестов от тестовых данных на стенде. Если по какой-то причине шаблонный проект исчезнет или будет модифицирован, то тесты упадут. Но в нашем случае риск минимален и оправдан.

2. Обогащение проекта данными

Тут с api все хорошо.

Можно добавлять задачи, подзадачи, создавать различные связи между ними, двигать по разным статусам.

Сами методы обогащения проекта выглядят примерно вот так:

public void setupEnv_JAGA4826() {         log.info("------подготовка JAGA4826");         jira.createIssue(jiraClient, jiraProjectId, adminLogin);         data.put("JAGA4826_jira_project_id", jiraProjectId);     }

3. Миграция тестового проекта в «Яга»

Теперь основная часть. Дело в том, что миграция любого проекта регресса длится в среднем от 2 до 10 минут. Учитывая, что каждый тест-кейс предполагает миграцию проекта, то времени на прогон сотни автотестов уйдет около 10 часов.

Как найти выход из этой ситуации?

Мы придумали самое простое решение:

  • Создаем проект на базе шаблона

  • После добавляем в него столько данных, сколько необходимо для каждого теста.

  • Мигрируем проект из Jira в «Яга».

  • После миграции запускаются автотесты.

Теперь у нас уходит порядка 15-20 минут на миграцию проекта и по несколько секунд на каждый автотест.

Как это реализовано на практике?

Наш основной стек — это Java, Junit5, RestAssured, Allure.

Проект может быть запущен со всеми тестами или с тестами с определенным тегом (например Smoke) или вовсе с 1 единственным тестом для отладки.

Чтобы каждый раз не создавать проект со всеми возможными тестовыми данными в самом начале анализируем тестовый план.

1. Создаем класс.

public class TestPlanListener implements TestExecutionListener{...}

Внутри которого переопределяем метод.

@Override public void testPlanExecutionStarted(TestPlan testPlan) {...}

2. Из тест-плана извлекаем весь список тэгов, которые есть в тестовом классе.

Тэги с предопределенным префиксом и есть наша цель. Каждый класс, который требует добавления тестовых данных в проект, содержит подобный тэг.

Полный текст класса тут
import org.junit.platform.engine.TestTag; import org.junit.platform.engine.support.descriptor.MethodSource; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.rtkit.converter.environment.TestEnv;  import java.util.List; import java.util.Optional; import java.util.Set;  public class TestPlanListener implements TestExecutionListener {     private static final Logger log = LoggerFactory.getLogger(TestPlanListener.class);     public static final String ENV_TAG_PREFIX = "setupEnv_";      @Override     public void testPlanExecutionStarted(TestPlan testPlan) {  TestExecutionListener.super.testPlanExecutionStarted(testPlan);          log.info("Подготовка тестового окружения");         Set<TestIdentifier> roots = testPlan.getRoots();         TestIdentifier root = getChildContainer(roots).get(0);         Set<TestIdentifier> children = testPlan.getChildren(root.getUniqueIdObject());         List<TestIdentifier> container = getChildContainer(children);         List<String> tags = getTestTags(container);         TestEnv.setup(tags);         log.info("Начало выполнения тестов");     }      private static List<String> getTestTags(List<TestIdentifier> containers) {         return containers.stream()                 .flatMap(container -> container.getTags()                         .stream()                         .map(TestTag::getName)                         .filter(tag -> tag.startsWith(ENV_TAG_PREFIX))                 )                 .toList();     }      private static List<MethodSource> getMethodSources(TestPlan testPlan, TestIdentifier container) {         return testPlan.getChildren(container.getUniqueIdObject())                 .stream()                 .map(TestIdentifier::getSource)                 .filter(Optional::isPresent)                 .map(source -> (MethodSource) source.get())                 .toList();     }       private List<TestIdentifier> getChildContainer(Set<TestIdentifier> testIdentifiers) {         List<TestIdentifier> containers = testIdentifiers                 .stream()                 .filter(TestIdentifier::isContainer)                 .toList();          if (containers.isEmpty()) {             throw new RuntimeException("Не найден контейнер в тест-плане");         }         return containers;     }      @Override     public void testPlanExecutionFinished(TestPlan testPlan) {   TestExecutionListener.super.testPlanExecutionFinished(testPlan);         log.info("Окончание выполнения тестов");         log.info("Удаление тестового окружения");     } }

3. Список тэгов передаем в метод, который занимается формированием тестового окружения.

4. Для каждого тэга вызываем метод обогащения тестовых данных.

public static void setupEnvByClassTag(String methodName) {         try {             TestEnv.class                     .getMethod(methodName)                     .invoke(new TestEnv());         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {             e.printStackTrace();         }     }

4. Проверка успешности миграции

Все этапы до этого можно назвать прекондицией. Теперь рассмотрим, как выглядят сами автотесты.

Здесь все достаточно тривиально.

Тестовый класс выглядит примерно так:
import io.qameta.allure.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import ru.rtkit.converter.environment.TestEnv; import ru.rtkit.converter.helpers.constants.Status; import ru.rtkit.converter.helpers.constants.Tags; import ru.rtkit.converter.helpers.jaga.JagaHelper; import ru.rtkit.converter.tests.BaseTest;  import static ru.rtkit.converter.helpers.AllureAssertions.*;  @DisplayName("Системное условие") @Tag("setupEnv_JAGA4826") public class UserInGroupConditionTests extends BaseTest {      String jagaProjectId;     String jiraProjectId;     String jagaTaskId;     String targetStatusId;     String actualTaskStatusId;      private JagaHelper jaga;      @BeforeEach     void beforeEach() {         jagaProjectId = TestEnv.data.get("jaga_project_id");         jiraProjectId = TestEnv.data.get("JAGA4826_jira_project_id");          jaga = new JagaHelper(jagaAdminClient, jagaUserClient, jagaProjectId, jiraProjectId);          jagaTaskId = jaga.asAdmin().getDefaultTaskId();         assertNotNull(jagaTaskId, "Не удалось получить идентификатор задачи с полем 'UserInGroupCondition'");          targetStatusId = jaga.asAdmin().getStatusId(Status.USER_IN_GROUP);         assertNotNull(targetStatusId, "Не удалось получить идентификатор статуса " + Status.USER_IN_GROUP);     }      @Test     @TmsLink("JAGA-T1921")     @DisplayName("Миграция условия UserInGroupCondition")     @Description("Миграция условия UserInGroupCondition")     @Story("JAGA-4826")     @Feature("JAGA-4528")     @Epic("JAGA-3793")     @Tag(Tags.REGRESS)     void migrateUserInGroupConditionTest() {         jaga.asAdmin().updateTaskStatus(200, jagaTaskId, targetStatusId);         actualTaskStatusId = jaga.asAdmin().getCurrentTaskStatus(jagaTaskId);         assertEquals(targetStatusId, actualTaskStatusId,                 "Статус задачи НЕ изменился");     } }

Заключение

Всю нехитрую логику можно уместить на этой схеме.

Схема

Схема

Скорее всего, было бы правильнее хранить метод обогащения проекта тестовыми данными в самом тестовом классе, но показалось, что удобнее собрать все в одном месте.

Возможно, кому-то этот пример принесет немного пользы.


ссылка на оригинал статьи https://habr.com/ru/articles/924902/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *