Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Extension в JUnit 5
Extension — это механизм в JUnit 5 для расширения функциональности тестового фреймворка. Это замена для JUnit 4 Rules и Runners, но значительно более мощная и гибкая.
Зачем нужны Extension
Extension позволяют:
- Инициализировать и очищать ресурсы
- Внедрять зависимости в тесты
- Регистрировать callback для событий жизненного цикла
- Параметризировать тесты
- Условно отключать/включать тесты
- Обрабатывать результаты тестов
Основные интерфейсы Extension
1. BeforeEachCallback / AfterEachCallback
public class DatabaseExtension implements
BeforeEachCallback,
AfterEachCallback {
private Connection connection;
@Override
public void beforeEach(ExtensionContext context) throws Exception {
// Выполняется перед каждым тестом
connection = DriverManager.getConnection(
"jdbc:h2:mem:test"
);
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
// Выполняется после каждого теста
if (connection != null && !connection.isClosed()) {
connection.close();
}
}
}
// Использование
@ExtendWith(DatabaseExtension.class)
class UserRepositoryTest {
@Test
void shouldFindUser() {
// connection готова к использованию
}
}
2. BeforeAllCallback / AfterAllCallback
public class ServerExtension implements
BeforeAllCallback,
AfterAllCallback {
private static TestServer server;
@Override
public void beforeAll(ExtensionContext context) throws Exception {
// Выполняется один раз перед всеми тестами в классе
server = new TestServer();
server.start();
}
@Override
public void afterAll(ExtensionContext context) throws Exception {
// Выполняется один раз после всех тестов
if (server != null) {
server.stop();
}
}
}
3. ParameterResolver
Внедряет зависимости в параметры теста:
public class RandomIntegerResolver implements ParameterResolver {
@Override
public boolean supportsParameter(
ParameterContext parameterContext,
ExtensionContext extensionContext) {
return parameterContext.getParameter()
.getType() == Integer.class &&
parameterContext.isAnnotated(Random.class);
}
@Override
public Object resolveParameter(
ParameterContext parameterContext,
ExtensionContext extensionContext) {
return new java.util.Random().nextInt(100);
}
}
// Custom аннотация
@Retention(RetentionPolicy.RUNTIME)
public @interface Random {}
// Использование
@ExtendWith(RandomIntegerResolver.class)
class RandomNumberTest {
@Test
void shouldGenerateRandomNumber(@Random Integer number) {
assertThat(number).isBetween(0, 99);
}
}
4. TestInstancePostProcessor
Обрабатывает экземпляр теста после создания:
public class InjectDependencyExtension
implements TestInstancePostProcessor {
@Override
public void postProcessTestInstance(
Object testInstance,
ExtensionContext context) throws Exception {
// Внедрение mock объектов
Field[] fields = testInstance.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(InjectMock.class)) {
field.setAccessible(true);
field.set(testInstance, Mockito.mock(field.getType()));
}
}
}
}
5. ExecutionCondition
Условно отключает/включает тесты:
public class WindowsOnlyExtension implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(
ExtensionContext context) {
String osName = System.getProperty("os.name");
if (osName.contains("Windows")) {
return ConditionEvaluationResult.enabled(
"Test runs on Windows"
);
}
return ConditionEvaluationResult.disabled(
"Test runs only on Windows"
);
}
}
@ExtendWith(WindowsOnlyExtension.class)
class WindowsSpecificTest {
@Test
void shouldWorkOnWindows() {
// Выполнится только на Windows
}
}
Встроенные Extension в JUnit 5
@TempDir — для временных файлов:
class FileOperationTest {
@Test
void shouldCreateFile(@TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("test.txt");
Files.write(file, "content".getBytes());
assertThat(file).exists();
}
}
@RegisterExtension — программная регистрация:
class ExtensionRegistrationTest {
@RegisterExtension
static DatabaseExtension dbExtension = new DatabaseExtension();
@Test
void shouldAccessDatabase() {
// dbExtension инициализирован
}
}
Практический пример: Custom Extension
public class LoggingExtension implements
BeforeEachCallback,
AfterEachCallback,
TestExecutionExceptionHandler {
@Override
public void beforeEach(ExtensionContext context) {
String testName = context.getDisplayName();
System.out.println("[BEFORE] " + testName);
}
@Override
public void afterEach(ExtensionContext context) {
String testName = context.getDisplayName();
System.out.println("[AFTER] " + testName);
}
@Override
public void handleTestExecutionException(
ExtensionContext context,
Throwable throwable) throws Throwable {
System.err.println("[ERROR] " + context.getDisplayName());
throw throwable; // перебросить исключение
}
}
@ExtendWith(LoggingExtension.class)
class UserServiceTest {
@Test
void shouldCreateUser() {
// Логирование автоматически
}
}
Extension vs Rules в JUnit 4
JUnit 4 (Rules):
class OldTest {
@Rule
public TestRule rule = new TestRule() {
// ограниченный функционал
};
}
JUnit 5 (Extension):
@ExtendWith(MyExtension.class)
class NewTest {
// намного более мощный механизм
}
Best Practices
1. Создавай специализированные Extension
// ✅ Хорошо — специфичный Extension
public class PostgresTestExtension implements BeforeAllCallback {
// специфичная реализация
}
// ❌ Плохо — общий Extension на все случаи
public class GenericExtension implements
BeforeAllCallback,
BeforeEachCallback,
AfterEachCallback,
TestExecutionExceptionHandler {
// слишком много ответственности
}
2. Используй CompositeExtension для комбинации
public class TestContainerExtension implements BeforeAllCallback {
// Extension, использующий другие Extension
}
3. Документируй Extension поведение
/**
* Extension для управления PostgreSQL Docker контейнером.
*
* Создаёт контейнер перед тестами и удаляет после.
* Предоставляет connection string через ExtensionContext.
*/
public class PostgresExtension implements BeforeAllCallback {
// ...
}
Extension — это современный и мощный способ расширения JUnit 5 функциональности, позволяющий создавать чистые и переиспользуемые тестовые инструменты.