Spring Boot 2.2.5.RELEASE
Mocking a @Cacheable
method with @SpyBean
results in null
when calling method.
Example:
@Service
public class SomeService{
@Cacheable("somecache")
public String method() {
return "not";
}
}
@SpringBootTest
@TestExecutionListeners(MockitoTestExecutionListener.class)
public class TestClass extends AbstractTestNGSpringContextTests {
@SpyBean SomeService spybean;
@Test
public void test(){
doReturn( "expected" ).when( spybean ).method();
assertEquals( spybean.method(), "expected" ); //fails
}
}
Comment From: wilkinsona
Having copied those two classes into a minimal sample application configured to use TestNG, I cannot reproduce the problem. If you would like us to spend some more time investigating, please spend some time providing a complete and minimal sample that reproduces the problem. You can share it with us by zipping it up and attaching it to this issue or by pushing it to a separate repository on GitHub.
Comment From: cdalexndr
https://github.com/cdalexndr/spring-boot-issue-20426
just run gradlew test
I forgot to mention that I'm using aspectj. It may fail only when using aspectj mode.
Comment From: mbhave
Another SpyBean
issue that might be related: spring-projects/spring-framework#24724
Comment From: wilkinsona
Thanks for the sample. As @mbhave suspected, this appears to be very similar to https://github.com/spring-projects/spring-framework/issues/24724. Here's a sample that reproduces the problem without any involvement from Spring Boot:
package example;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Application.class)
public class SomeServiceTest {
@Spy
SomeService someService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testCacheable() {
doReturn("yes").when(someService).cacheableMethod();
assertThat(someService.cacheableMethod()).isEqualTo("yes");
}
@Test
public void testNormal() {
doReturn("yes").when(someService).normalMethod();
assertThat(someService.normalMethod()).isEqualTo("yes");
}
}
@Service
class SomeService {
@Cacheable("someCache")
public String cacheableMethod() {
return "not";
}
public String normalMethod() {
return "not";
}
}
@ComponentScan
@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
class Application {
@Bean
ConcurrentMapCacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
}
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.aspectj', name: 'aspectjweaver'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
implementation 'org.springframework:spring-aspects'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
File springAgent = configurations.runtimeClasspath.filter { f -> f.name.matches("aspectjweaver.*\\.jar") }.iterator().next();
jvmArgs += ["-javaagent:" + springAgent.absolutePath]
}
Comment From: snicoll
Closing as a duplicate of https://github.com/spring-projects/spring-framework/issues/24724. I'll drop a note there to make sure to review the reproducer above.