javamockitojunit5spring-hateoas

How to solve 'Cannot subclass final class java.lang.reflect.Method'?


I have this Junit 5 test code:

@InjectMocks
ReportAssembler reportAssembler;

private ReportingReturnResponse reportingReturnResponse;
private ReturnReport returnReport;
private StaticPathLinkBuilder staticPathLinkBuilder;

@Test
public void testMergeReturnCompletesSuccessfully() {
    ReportingResource reportResource = reportAssembler.mergeReturn(returnReport, this.reportingResource);
    assertNotNull(reportResource.getReturnResource());
}

The called code:

public ReportingResource mergeReturn(ReturnReport returnReport, ReportingResource reportingResource) {
    Link link = staticPathLinkBuilder.linkTo(
        methodOn(ReturnController.class).getReturnById(
            returnReport.getAccount().getId(),
            returnReport.getReturn().getId())
    ).withSelfRel();

    return reportingResource;
}

public LinkBuilder linkTo(Object dummyInvocation) {
    LastInvocationAware lastInvocationAware = (LastInvocationAware) dummyInvocation;

    MethodInvocation methodInvocation = lastInvocationAware.getLastInvocation();

    StaticPathLinkBuilder staticPathLinkBuilder = getThis();
    return staticPathLinkBuilder.linkTo(methodInvocation.getMethod(), methodInvocation.getArguments());
}

The line return staticPathLinkBuilder.linkTo(methodInvocation.getMethod(), methodInvocation.getArguments()) throws an exception Caused by: java.lang.IllegalArgumentException: Cannot subclass final class java.lang.reflect.Method

How I can solve it?

The stacktrace:

org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class java.lang.reflect.Method: Common causes of this problem include using a final class or a non-visible class
    at org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:237)
    at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:167)
    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
    at org.springframework.hateoas.server.core.DummyInvocationUtils.getProxyWithInterceptor(DummyInvocationUtils.java:207)
    at org.springframework.hateoas.server.core.DummyInvocationUtils$InvocationRecordingMethodInterceptor.invoke(DummyInvocationUtils.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
    at jdk.proxy3/jdk.proxy3.$Proxy81.getMethod(Unknown Source)
    at com.util.StaticPathLinkBuilder.linkTo(StaticPathLinkBuilder.java:56)
    at com.util.StaticPathLinkBuilder.linkTo(StaticPathLinkBuilder.java:42)
    at com.assembler.ReportingAssembler.mergeReturn(ReportingAssembler.java:233)
    at com.assembler.ReportingAssemblerReturnTest.testMergeReturnCompletesSuccessfully(ReportingAssemblerReturnTest.java:50)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class java.lang.reflect.Method
    at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:653)
    at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:35)
    at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:26)
    at org.springframework.cglib.core.ClassLoaderAwareGeneratorStrategy.generate(ClassLoaderAwareGeneratorStrategy.java:76)
    at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:366)
    at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:575)
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107)
    at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
    at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57)
    at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130)
    at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317)
    at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:562)
    at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:407)
    at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:62)
    at org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:228)
    at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:167)
    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
    at org.springframework.hateoas.server.core.DummyInvocationUtils.getProxyWithInterceptor(DummyInvocationUtils.java:207)
    at org.springframework.hateoas.server.core.DummyInvocationUtils$InvocationRecordingMethodInterceptor.invoke(DummyInvocationUtils.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
    at jdk.proxy3/jdk.proxy3.$Proxy81.getMethod(Unknown Source)
    at com.util.StaticPathLinkBuilder.linkTo(StaticPathLinkBuilder.java:56)
    at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733)
    at org.mockito.internal.util.reflection.InstrumentationMemberAccessor$Dispatcher$ByteBuddy$ADlM6biV.invokeWithArguments(Unknown Source)
    at org.mockito.internal.util.reflection.InstrumentationMemberAccessor.invoke(InstrumentationMemberAccessor.java:251)
    at org.mockito.internal.util.reflection.ModuleMemberAccessor.invoke(ModuleMemberAccessor.java:55)
    at org.mockito.internal.creation.bytebuddy.MockMethodAdvice.tryInvoke(MockMethodAdvice.java:314)
    at org.mockito.internal.creation.bytebuddy.MockMethodAdvice$RealMethodCall.invoke(MockMethodAdvice.java:234)
    at org.mockito.internal.invocation.InterceptedInvocation.callRealMethod(InterceptedInvocation.java:142)
    at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:45)
    at org.mockito.Answers.answer(Answers.java:90)
    at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:111)
    at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
    at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
    at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
    at org.mockito.internal.creation.bytebuddy.MockMethodAdvice.handle(MockMethodAdvice.java:134)
    ... 6 more

Solution

  • You are using sensitive API, that is not intended for public use while it is public.
    The library authors explain this in this GitHub issue.
    They also suggest a workaround in the same github issue - use DummyInvocationUtils.getLastInvocationAware(...).

    For your specific case, replace this:

    LastInvocationAware lastInvocationAware = (LastInvocationAware) dummyInvocation;
    

    with:

    LastInvocationAware lastInvocationAware = DummyInvocationUtils.getLastInvocationAware(dummyInvocation);