Хабрахабр

Настройка состава JUnit5 тестов с помощью application.properties

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

Теперь представьте, что не все тесты должны проходить в этих окружениях — кажому свой набор тестов.

И предпочтительней настроить выбор, какие тесты должны выполняться, в… файле application.properties — кажому тесту свой переключатель "вкл/выкл".

Звучит здорово, не правда ли?

Тогда добро пожаловать под кат, где мы все это и реализуем с помощью SpringBoot 2 и JUnit 5.

Сперва давайте выключим JUnit 4, который поставляется в SpringBoot 2 по-умолчанию, и включим JUnit 5.

Для этого внесем изменения в pom.xml:

<dependencies> <!--...--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.3.2</version> <scope>test</scope> </dependency> <!--...-->
</dependencies>

Напомню, что значения этого свойства мы собираемся хранить в файле application.properties. Мы хотим аннотировать каждый тест простой аннотацией со свойством, указывающим на то, включен ли тест или нет.

Аннотация

Создадим аннотацию:

@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(TestEnabledCondition.class)
public @interface TestEnabled { String property();
}

Обработка аннотации

Без обработчика аннотации не обойтись.

public class TestEnabledCondition implements ExecutionCondition else { return ConditionEvaluationResult.disabled("Disabled by property: "+property); } }).orElse( ConditionEvaluationResult.disabled("Disabled - property <"+property+"> not set!") ); }).orElse( ConditionEvaluationResult.enabled("Enabled by default") ); }
}

Необходимо создать класс (без аннотации Spring-а @Component), который реализует интерфейс ExecutionCondition.

В этом классе необходимо реализовать один метод этого интерфейса — ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context).

Этот метод принимает контекст выполняемого JUnit теста и возвращает условие — должен ли тест быть запущен или нет.

Прочитать подробней по условное выполнение тестов JUnit5 можно в официальной документации.

Но как нам проверить значение свойства, которое прописано в application.properties в таком случае?

Получение доступа к контексту Spring из контекста JUnit

Вот таким образом мы можем получить окружение Spring, с которым был запущен наш JUnit тест, из ExtensionContext.

Environment environment = SpringExtension.getApplicationContext(context).getEnvironment();

Можете взглянуть на полный код класса TestEnabledCondition.

Давайте создадим несколько тестов и попробуем управлять их запуском:

@SpringBootTest
public class SkiptestApplicationTests { @TestEnabled(property = "app.skip.test.first") @Test public void testFirst() { assertTrue(true); } @TestEnabled(property = "app.skip.test.second") @Test public void testSecond() { assertTrue(false); } }

Наш application.properties файл при этом выглядит так:

app.skip.test.first=true
app.skip.test.second=false

Итак...

Результат запуска:

Поэтому резонно их префикс вынести на уровень класса тестов — в отдельную аннотацию. Писать перед каждым тестом полные названия свойств из application.properties — утомительное занятие.

Создадим annotation для хранения префиксов — TestEnabledPrefix:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestEnabledPrefix { String prefix();
}

Обработка и использование аннотации TestEnabledPrefix

Приступим к обработке новой аннотации.

Давайте создадим вспомогательный класс AnnotationDescription

С помощью этого класса мы сможем хранить имя свойства из application.properties и его значение.

public class TestEnabledCondition implements ExecutionCondition { static class AnnotationDescription { String name; Boolean annotationEnabled; AnnotationDescription(String prefix, String property) { this.name = prefix + property; } String getName() { return name; } AnnotationDescription setAnnotationEnabled(Boolean value) { this.annotationEnabled = value; return this; } Boolean isAnnotationEnabled() { return annotationEnabled; } } /* ... */
}

мы собираемся использовать lambda-выражения. Нам этот класс пригодится, т.к.

Создадим метод, который извлечет нам значение свойства "префикс" из аннотации класса TestEnabledPrefix

public class TestEnabledCondition implements ExecutionCondition { /* ... */ private AnnotationDescription makeDescription(ExtensionContext context, String property) { String prefix = context.getTestClass() .map(cl -> cl.getAnnotation(TestEnabledPrefix.class)) .map(TestEnabledPrefix::prefix) .map(pref -> !pref.isEmpty() && !pref.endsWith(".") ? pref + "." : "") .orElse(""); return new AnnotationDescription(prefix, property); } /* ... */ }

И теперь проверим значение свойства из application.properties по имени, указанном в аннотации теста

public class TestEnabledCondition implements ExecutionCondition { /* ... */ @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { Environment environment = SpringExtension.getApplicationContext(context).getEnvironment(); return context.getElement() .map(e -> e.getAnnotation(TestEnabled.class)) .map(TestEnabled::property) .map(property -> makeDescription(context, property)) .map(description -> description.setAnnotationEnabled(environment.getProperty(description.getName(), Boolean.class))) .map(description -> { if (description.isAnnotationEnabled()) { return ConditionEvaluationResult.enabled("Enabled by property: "+description.getName()); } else { return ConditionEvaluationResult.disabled("Disabled by property: "+description.getName()); } }).orElse( ConditionEvaluationResult.enabled("Enabled by default") ); } }

Полный код класса доступен по ссылке.

Использование новой аннотации

Теперь применим нашу аннотацию к тест-классу:

@SpringBootTest
@TestEnabledPrefix(property = "app.skip.test")
public class SkiptestApplicationTests { @TestEnabled(property = "first") @Test public void testFirst() { assertTrue(true); } @TestEnabled(property = "second") @Test public void testSecond() { assertTrue(false); } }

Теперь наш код тестов стал чище и проще.

Хочу выразить благодарность пользователям reddit-а за их советы:

1) dpash за совет
2) BoyRobot777 за совет

P.S.

Английский вариант опубликован в README.md файле рядом с кодом проекта. Статья является авторским переводом.

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть