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

Стояла задача автоматизации регресса для сервиса миграции из системы 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,
                "Статус задачи НЕ изменился");
    }
}

Заключение

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

Схема
Схема

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

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

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