Mocking a class with @Transactional method with Mo

2019-07-13 19:52发布

问题:

I have a service, a bean, that contains a @Transactional method:

public class InMessageService {

...

    @Transactional

    public boolean retryInMessage(String messageId) {

    ...

    }

}

For testing, I try to mock that service with Mockito:

@Bean
@Primary
public InMessageService inMessageService() {
    return Mockito.mock(InMessageService.class);
}

The result of this is the following exception when I start the test:

   Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB
subclass of class somePackage.InMessageService$MockitoMock$156222813: Common
causes of this problem include using a final class or a non-visible class;nested exception is
org.springframework.cglib.core.CodeGenerationException: java.lang.NoClassDefFoundError-->
somePath/InMessageService$MockitoMock$156222813

I want to mention that the same code was working with spring-boot 1.2.1 and Mockito 1.10.19. I try to run the above code with spring boot 2.1.1 and Mockito 2.23.0

My observations so far:

  • No matter what Mockito version between 2.1.0 and 2.23.0 I use, the exception is the same. I cannot (and don't want to) use older versions of Mockito as the project does not compile any more
  • If I temporary remove the @Transactional annotation, the exception is not thrown.

Any ideas what has to be adjusted with the upgrade of spring boot so the tests work again ?

Thank you !

回答1:

Since version 2.1.0 Mockito retains annotations on proxied methods. This means that Spring attempts to proxy the mock class that declares a transactional annotation and this fails, because the mocking method is final.

Before, Mockito stripped these annotations what would have caused any real method call to fail due to the missing transaction.

To avoid this, you would need to strip the annotations of the mock. You can do so using MockSettings.withoutAnnotations.