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

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