JUnit, @ControllerAdvice и отсутствие проверенных исключений в Kotlin

Я написал советник по валидации в Котлине, который бросает EntityValidationException когда проверка не проходит:

@Aspect
@Named
class ValidationAdvisor
@Inject constructor(val validator: EntityValidator) {
    @Around(EVERY_SAVE_AND_UPDATE_TO_DATABASE)
    fun validate(point: ProceedingJoinPoint): Any {
        val result: List<ConstraintViolation<Any>> = validator.validate(getEntity(point))
        if (isEntityValid(result))
            return point.proceed()

        throw EntityValidationException(
            violationInfos = result as List<ConstraintViolationInfo>
        )
    }

    private fun getEntity(point: ProceedingJoinPoint): Any {
        return point.args[0]
    }

    private fun isEntityValid(result: List<ConstraintViolation<Any>>): Boolean {
        return result.isEmpty()
    }

    companion object {
        const val EVERY_SAVE_AND_UPDATE_TO_DATABASE = "execution(* beans.repositories.BaseRepository.save(..))"
    }
}

У меня есть тест JUnit (я пишу некоторые части на Kotlin и некоторые на Java для изучения Kotlin в существующем частном проекте), который проверяет, было ли выброшено исключение:

@Test(expected = EntityValidationException.class)
public void test_WhenNameIsNotPresent_ThenExceptionIsThrown() throws Exception {
    repository.save(/* entity to save */);
}

Но потом я увидел, что тест не пройден, потому что он генерирует UndeclaredThrowableException вместо EntityValidationException, Далее я прочитал, что у Котлина нет проверенного исключения. Я думаю, что не только у меня есть эта проблема - я уверен, что способ обработки исключений с помощью @ControllerAdvice в Spring и Java также может быть проблематичным, когда мы решим переключиться на Kotlin в производственных проектах - потому что он опирается на класс, который выдается, и бросание UndeclaredThrowableException отними у нас эту возможность. Есть ли способ обойти это поведение, чтобы убедиться, что возможно переключиться на Kotlin и старый Spring способ обработки исключений (и модульные тесты для ValidationAdvisor) останется без изменений? Спасибо заранее за любую помощь.

PS Github репо для проекта доступно здесь: Playgroud для Kotlin, Spring Data и т. Д.

PS2 Stacktrace:

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.reflect.UndeclaredThrowableException
    at com.sun.proxy.$Proxy65.save(Unknown Source)
    at ActorValidationTest.test_WhenNameIsNotPresent_ThenExceptionIsThrown(ActorValidationTest.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19)
    ... 26 more
Caused by: beans.validator.exceptions.EntityValidationException
    at beans.aop.validation.ValidationAdvisor.validate(ValidationAdvisor.kt:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    ... 37 more

Для меня выглядит EntityValidationException (который я хочу бросить) причины UndeclaredThrowableException, EntityValidationException Код класса Kotlin:

package beans.validator.exceptions

import beans.validator.constraintViolation.ConstraintViolationInfo

class EntityValidationException (val violationInfos: List<ConstraintViolationInfo>) : Exception() {
}

PS3 это тоже не сработало:

@Around(EVERY_SAVE_AND_UPDATE_TO_DATABASE)
@Throws(EntityValidationException::class)
fun validate(point: ProceedingJoinPoint): Any /* rest of code*/

PS4 Может проблема в том что @Aspect является @Around Spring Data репозиторий?

companion object {
        const val EVERY_SAVE_AND_UPDATE_TO_DATABASE = "execution(* beans.repositories.BaseRepository.save(..))"
    }
....
@NoRepositoryBean
public interface BaseRepository<ENTITY, IDENTIFIER extends Serializable>
        extends JpaRepository<ENTITY, IDENTIFIER>, JpaSpecificationExecutor<ENTITY> {
}

1 ответ

Решение

Вы пытаетесь сделать метод save() или ваш репозиторий генерирует проверенное исключение, которое он не объявляет. Поэтому, даже если аспект заявляет об этом, это не может работать. Вы должны расширить свое исключение RuntimeException,

Другие вопросы по тегам