当前位置: 动力学知识库 > 问答 > 编程问答 >

java - JUnit, @ControllerAdvice and lack of checked exceptions in Kotlin

问题描述:

I wrote a validation advisor in Kotlin which throws EntityValidationException when validation fails:

@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(..))"

}

}

I have a JUnit test (I write some parts in Kotlin and some in Java, to learn Kotlin in existing private project) which checks if exception is thrown:

@Test(expected = EntityValidationException.class)

public void test_WhenNameIsNotPresent_ThenExceptionIsThrown() throws Exception {

repository.save(/* entity to save */);

}

But then I saw that test fails because it throws UndeclaredThrowableException instead of EntityValidationException. Next, I read that Kotlin does not have checked exception. I think not only me have this problem - I am pretty sure that way of handling exceptions with @ControllerAdvice in Spring and Java also may be problematic when we decide to switch to Kotlin in production projects - because it rely on class which is thrown, and throwing UndeclaredThrowableException take away from us this possibility. Is there a way to by-pass this behaviour, to make sure that is possible to switch to Kotlin and old Spring way of handling exceptions (and unit tests for ValidationAdvisor) will remain unchanged? Thank you in advance for any help.

P.S. Github repo for project is available here: Playgroud for Kotlin, Spring Data etc..

P.S.2 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

For me looks like EntityValidationException (which I want to be thrown) causes UndeclaredThrowableException.

EntityValidationException Kotlin class code:

package beans.validator.exceptions

import beans.validator.constraintViolation.ConstraintViolationInfo

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

}

P.S.3 This also didn't work:

@Around(EVERY_SAVE_AND_UPDATE_TO_DATABASE)

@Throws(EntityValidationException::class)

fun validate(point: ProceedingJoinPoint): Any /* rest of code*/

P.S.4 Maybe the problem is that @Aspect is @Around Spring Data repository?

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> {

}

网友答案:

You're trying to make the method save() or your repository throw a checked exception that it doesn't declare. So even if the aspect declares it, that can't work. You need to make your exception extend RuntimeException.

分享给朋友:
您可能感兴趣的文章:
随机阅读: