Skip to main content
unit-test-caching Provides patterns for unit testing Spring Cache annotations (@Cacheable, @CachePut, @CacheEvict). Generates test code that mocks cache managers, verifies cache hit/miss behavior, tests cache key generation with SpEL expressions, validates eviction strategies, and checks conditional caching scenarios. Triggers: caching tests, test Spring cache, mock cache, Spring Boot caching, cache hit/miss verification, @Cacheable testing.
Zur Installation springen Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-cachingDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Mehr aus diesem Repository Posts review findings from a JSON file as inline comments on a GitHub Pull Request, attaching each comment to its file and line. Use when you have a list/JSON of review findings (each with a file path, line number, and a message such as summary/failure_scenario) and want them published on a PR as inline review comments. Triggers include "post these review comments on the PR", "associate comments to files in the PR", "publish review findings to PR
typescript-security-review Provides security review capability for TypeScript/Node.js applications, validates code against XSS, injection, CSRF, JWT/OAuth2 flaws, dependency CVEs, and secrets exposure. Use when performing security audits, before deployment, reviewing authentication/authorization implementations, or ensuring OWASP compliance for Express, NestJS, and Next.js. Triggers on "security review", "check for security issues", "TypeScript security audit".
Initialize Spec-Driven Development context — detects tech stack, conventions, architecture patterns, and bootstraps persistence backends. Triggers on 'sdd-init', 'init sdd', 'setup sdd', 'initialize sdd', 'setup project', 'initialize project context'. Creates/updates docs/specs/architecture.md & ontology.md (Constitution), and populates knowledge-graph.json.
Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
name unit-test-caching description Provides patterns for unit testing Spring Cache annotations (@Cacheable, @CachePut, @CacheEvict). Generates test code that mocks cache managers, verifies cache hit/miss behavior, tests cache key generation with SpEL expressions, validates eviction strategies, and checks conditional caching scenarios. Triggers: caching tests, test Spring cache, mock cache, Spring Boot caching, cache hit/miss verification, @Cacheable testing. allowed-tools Read, Write, Bash, Glob, Grep
Unit Testing Spring Caching
Overview
This skill provides patterns for unit testing Spring caching annotations (@Cacheable, @CacheEvict, @CachePut) without full Spring context. It covers cache hits/misses, invalidation, key generation, and conditional caching using in-memory ConcurrentMapCacheManager.
When to Use
Writing unit tests for @Cacheable method behavior
Verifying @CacheEvict cache invalidation works correctly
Testing @CachePut cache updates
Validating cache key generation from SpEL expressions
Testing conditional caching with unless/condition parameters
Mocking cache managers in fast unit tests without Redis
Instructions
Configure in-memory CacheManager : Use ConcurrentMapCacheManager for tests
Set up test fixtures : Mock repository and create service instance in @BeforeEach
Verify repository call counts : Use times(n) assertions to confirm cache behavior
Test cache hit : Call method twice, verify repository called once
Test cache miss : Verify repository called on each invocation
Test eviction : After @CacheEvict, verify repository called again on next read
Test key generation : Verify compound keys from SpEL expressions
Validate conditional caching : Test unless (null results) and condition (parameter-based)
Run test → If cache not working: verify @EnableCaching annotation present
If proxy issues: ensure method calls go through Spring proxy (no direct this calls)
If key mismatches: log actual cache key and compare with @Cacheable(key="...") expression
Examples
Maven <dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-cache</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-test</artifactId >
<scope > test</scope >
</dependency >
Gradle dependencies {
implementation("org.springframework.boot:spring-boot-starter-cache" )
testImplementation("org.springframework.boot:spring-boot-starter-test" )
}
Testing @Cacheable (Cache Hit/Miss)
@Service
public class UserService {
private final UserRepository userRepository;
public UserService (UserRepository userRepository) {
this .userRepository = userRepository;
}
@Cacheable("users")
public User getUserById (Long id) {
return userRepository.findById(id).orElse(null );
}
}
class UserServiceCachingTest {
private UserRepository userRepository;
private UserService userService;
@BeforeEach
void setUp () {
userRepository = mock(UserRepository.class);
userService = new UserService (userRepository);
}
@Test
void shouldCacheUserAfterFirstCall () {
User user = new User (1L , "Alice" );
when (userRepository.findById(1L )).thenReturn(Optional.of(user));
User firstCall = userService.getUserById(1L );
User secondCall = userService.getUserById(1L );
assertThat(firstCall).isEqualTo(secondCall);
verify(userRepository, times(1 )).findById(1L );
}
@Test
void shouldInvokeRepositoryOnCacheMiss () {
when (userRepository.findById(1L )).thenReturn(Optional.of(new User (1L , "Bob" )));
userService.getUserById(1L );
userService.getUserById(1L );
verify(userRepository, times(2 )).findById(1L );
}
}
Testing @CacheEvict
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService (ProductRepository productRepository) {
this .productRepository = productRepository;
}
@Cacheable("products")
public Product getProductById (Long id) {
return productRepository.findById(id).orElse(null );
}
@CacheEvict("products")
public void deleteProduct (Long id) {
productRepository.deleteById(id);
}
}
class ProductCacheEvictTest {
private ProductRepository productRepository;
private ProductService productService;
@BeforeEach
void setUp () {
productRepository = mock(ProductRepository.class);
productService = new ProductService (productRepository);
}
@Test
void shouldEvictProductFromCacheWhenDeleted () {
Product product = new Product (1L , "Laptop" , 999.99 );
when (productRepository.findById(1L )).thenReturn(Optional.of(product));
productService.getProductById(1L );
productService.deleteProduct(1L );
productService.getProductById(1L );
verify(productRepository, times(2 )).findById(1L );
}
@Test
void shouldClearAllEntriesWithAllEntriesTrue () {
Product product1 = new Product (1L , "Laptop" , 999.99 );
Product product2 = new Product (2L , "Mouse" , 29.99 );
when (productRepository.findById(anyLong())).thenAnswer(i ->
Optional.of(new Product (i.getArgument(0 ), "Product" , 10.0 )));
productService.getProductById(1L );
productService.getProductById(2L );
productService.clearAllProducts();
productService.getProductById(1L );
productService.getProductById(2L );
verify(productRepository, times(4 )).findById(anyLong());
}
}
Testing @CachePut @Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService (OrderRepository orderRepository) {
this .orderRepository = orderRepository;
}
@Cacheable("orders")
public Order getOrder (Long id) {
return orderRepository.findById(id).orElse(null );
}
@CachePut(value = "orders", key = "#order.id")
public Order updateOrder (Order order) {
return orderRepository.save(order);
}
}
class OrderCachePutTest {
private OrderRepository orderRepository;
private OrderService orderService;
@BeforeEach
void setUp () {
orderRepository = mock(OrderRepository.class);
orderService = new OrderService (orderRepository);
}
@Test
void shouldUpdateCacheWhenOrderIsUpdated () {
Order original = new Order (1L , "Pending" , 100.0 );
Order updated = new Order (1L , "Shipped" , 100.0 );
when (orderRepository.findById(1L )).thenReturn(Optional.of(original));
when (orderRepository.save(updated)).thenReturn(updated);
orderService.getOrder(1L );
orderService.updateOrder(updated);
Order cachedOrder = orderService.getOrder(1L );
assertThat(cachedOrder.getStatus()).isEqualTo("Shipped" );
}
}
Testing Conditional Caching @Service
public class DataService {
private final DataRepository dataRepository;
public DataService (DataRepository dataRepository) {
this .dataRepository = dataRepository;
}
@Cacheable(value = "data", unless = "#result == null")
public Data getData (Long id) {
return dataRepository.findById(id).orElse(null );
}
@Cacheable(value = "users", condition = "#id > 0")
public User getUser (Long id) {
return dataRepository.findById(id).map(u -> new User (u.getId(), u.getName())).orElse(null );
}
}
class ConditionalCachingTest {
@Test
void shouldNotCacheNullResults () {
DataRepository dataRepository = mock(DataRepository.class);
when (dataRepository.findById(999L )).thenReturn(Optional.empty());
DataService service = new DataService (dataRepository);
service.getData(999L );
service.getData(999L );
verify(dataRepository, times(2 )).findById(999L );
}
@Test
void shouldNotCacheWhenConditionIsFalse () {
DataRepository dataRepository = mock(DataRepository.class);
when (dataRepository.findById(-1L )).thenReturn(Optional.of(new Data (-1L , "Test" )));
DataService service = new DataService (dataRepository);
service.getUser(-1L );
service.getUser(-1L );
verify(dataRepository, times(2 )).findById(-1L );
}
}
Testing Cache Keys with SpEL @Service
public class InventoryService {
private final InventoryRepository inventoryRepository;
public InventoryService (InventoryRepository inventoryRepository) {
this .inventoryRepository = inventoryRepository;
}
@Cacheable(value = "inventory", key = "#productId + '-' + #warehouseId")
public InventoryItem getInventory (Long productId, Long warehouseId) {
return inventoryRepository.findByProductAndWarehouse(productId, warehouseId);
}
}
class CacheKeyTest {
@Test
void shouldUseCorrectCacheKeyForDifferentCombinations () {
InventoryRepository repository = mock(InventoryRepository.class);
InventoryItem item = new InventoryItem (1L , 1L , 100 );
when (repository.findByProductAndWarehouse(1L , 1L )).thenReturn(item);
InventoryService service = new InventoryService (repository);
service.getInventory(1L , 1L );
service.getInventory(1L , 1L );
verify(repository, times(1 )).findByProductAndWarehouse(1L , 1L );
service.getInventory(2L , 1L );
verify(repository, times(2 )).findByProductAndWarehouse(any(), any());
}
}
Best Practices
Mock repository calls : Use verify(mock, times(n)) to assert cache behavior
Test both hit and miss scenarios : Don't just test the happy path
Clear cache state : Reset between tests to avoid flaky results
Use ConcurrentMapCacheManager : Fast, no external dependencies
Verify eviction : Always test that @CacheEvict actually invalidates cached data
Constraints and Warnings
@Cacheable requires proxy : Direct method calls (this.method()) bypass caching - use dependency injection
Cache key collisions : Compound keys from SpEL must be unique per dataset
Null caching : Null results are cached by default - use unless = "#result == null" to exclude
@CachePut always executes : Unlike @Cacheable, it always runs the method
Memory usage : In-memory caches grow unbounded - consider TTL for long-running tests
Thread safety : ConcurrentMapCacheManager is thread-safe; distributed caches may require additional config
Troubleshooting Issue Solution Cache not working Verify @EnableCaching on test config Proxy bypass Use autowired/constructor injection, not direct this calls Key mismatch Log cache key with cache.getNativeKey() to debug SpEL Flaky tests Clear cache in @BeforeEach before each test
References