Light Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Review documentation and migration guide about changes in @AutoConfigureCache #48522

Closed
Closed
Review documentation and migration guide about changes in @AutoConfigureCache#48522
Assignees
Labels
Milestone

Description

Problem

While Upgrading from Spring Boot 3.5.8 to 4.0.0, I'm seeing minimal @WebMvcTest tests and other test slices (e.g. @DataJdbcTest) fail with the following exception.

No qualifying bean of type 'org.springframework.cache.CacheManager' available: no CacheResolver specified - register a CacheManager bean or remove the @EnableCaching annotation from your configuration.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cache.CacheManager' available: no CacheResolver specified - register a CacheManager bean or remove the @EnableCaching annotation from your configuration.
at app//org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:287)
at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1147)
at app//org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:983)
at app//org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:620)
at app//org.springframework.boot.SpringApplication.refresh(SpringApplication.java:765)
at app//org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:454)
at app//org.springframework.boot.SpringApplication.run(SpringApplication.java:321)

Example

I've included the abbreviated example below to demonstrate the problem.

@WebMvcTest(ProductController.class)
class ProductControllerTest {

@Autowired
private MockMvc mockMvc;

@Autowired
private ObjectMapper objectMapper;

@MockitoBean
private ProductService productService;

@Test
void getProduct_WhenProductExists_ReturnsProduct() throws Exception {
Product product = new Product(1L, "Test Product", "Test Description", 99.99);
when(productService.getProductById(1L)).thenReturn(Optional.of(product));

mockMvc.perform(get("/api/products/1"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Test Product"))
.andExpect(jsonPath("$.description").value("Test Description"))
.andExpect(jsonPath("$.price").value(99.99));
}

}

@RestController
@RequestMapping("/api/products")
public class ProductController {

private final ProductService productService;

public ProductController(ProductService productService) {
this.productService = productService;
}

@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
return productService.getProductById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

}

@Service
public class ProductService {

private final Map<Long, Product> dataStore = new ConcurrentHashMap<>();
private final AtomicLong idGenerator = new AtomicLong(1);

public ProductService() {
dataStore.put(1L, new Product(1L, "Sample Product", "A sample product", 99.99));
dataStore.put(2L, new Product(2L, "Another Product", "Another sample", 149.99));
idGenerator.set(3);
}

@Cacheable(value = "products", key = "#id")
public Optional<Product> getProductById(Long id) {
System.out.println("Fetching product from data store for id: " + id);
return Optional.ofNullable(dataStore.get(id));
}

}

@Configuration
public class CacheConfig {

@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("products");
}
}

@EnableCaching
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

Full Reproducer:

What appears to be happening is these test slices scan the @SpringBootApplication to pick up configurations and sees the @EnableCacheing and then aggressively tries to autoconfigure cacheing.

In Spring Boot 3.x, if cacheing was not configured, it would silently ignore @Cacheable annotation. But in Spring Boot 4.x it fails the test.

Expectation

OSS tests slices and minimal test slices should not fail when trying to configure AOP integrations like cacheing when the dependencies and configurations are provided not provided those annotations to work.

The workaround is move @EnableCacheing to another @Configuration so it's not picked up by OSS Test slice annotations. However, this is not ideal or intuitive for the user to be aware of delicate separations like this. This is one example, but there's probably many ways this fails if @EnableCacheing is seen in a minimal test, but we don't want to intentionally test Cacheing.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions