В статье хочу поделиться кейсом, с которым столкнулся на одном из проектов. И расскажу о своем варианте его решения.
Стояла задача автоматизации регресса для сервиса миграции из системы Jira во внутреннюю систему «Яга».
Если упростить, то типичный тест-кейс представлял из себя следующие шаги:
- Создание тестового проекта в Jira 
- Обогащение проекта данными 
- Миграция тестового проекта в «Яга» 
- Проверка успешности миграции 
Давайте детальнее разберем реализацию каждого пункта.
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,
                "Статус задачи НЕ изменился");
    }
}Заключение
Всю нехитрую логику можно уместить на этой схеме.

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