
Unit Test Caching
Generate fast Spring Boot unit tests that prove @Cacheable, @CachePut, and @CacheEvict behave without spinning up Redis.
Overview
Unit Test Caching is an agent skill for the Ship phase that generates Spring Cache unit tests using an in-memory CacheManager to verify hits, misses, evictions, and SpEL keys.
Install
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-cachingWhat is this skill?
- Unit-test Spring Cache annotations with in-memory ConcurrentMapCacheManager—no full Spring context or Redis.
- Verify repository call counts (times(n)) for cache hits, misses, puts, and evictions.
- Covers SpEL cache keys, conditional caching (unless/condition), and @CacheEvict strategies.
- Mocks repository layer in @BeforeEach fixtures for isolated service tests.
- Triggers on caching tests, @Cacheable testing, and Spring Boot caching scenarios.
- Covers @Cacheable, @CachePut, and @CacheEvict annotation testing
- Uses ConcurrentMapCacheManager for in-memory tests without Redis
Adoption & trust: 1.2k installs on skills.sh; 271 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You added @Cacheable to a service but only have slow integration tests—or none—and cannot prove eviction and key logic work.
Who is it for?
Solo Java/Spring Boot builders adding or refactoring cache annotations who want fast, deterministic unit coverage.
Skip if: Teams needing Redis cluster failover, serialization, or TTL integration proofs—that requires dedicated infra tests outside this skill’s scope.
When should I use this skill?
Caching tests, test Spring cache, mock cache, Spring Boot caching, cache hit/miss verification, or @Cacheable testing.
What do I get? / Deliverables
You get JUnit tests with mocked repositories and assertable call counts that document expected cache hit, miss, put, and evict behavior before deploy.
- JUnit test classes with CacheManager setup and repository mocks
- Assertions on invocation counts and cache state for hit/miss/evict scenarios
Recommended Skills
Journey fit
Ship → testing is where caching correctness must be proven before production deploys and perf regressions slip through. Unit-level cache hit/miss and eviction tests belong in the testing subphase—not infra provisioning.
How it compares
Focused test-generation patterns for annotations—not a full Spring TestContext or Testcontainers caching suite.
Common Questions / FAQ
Who is unit-test-caching for?
Spring Boot maintainers and indie backend devs writing service-layer cache logic who want agent help producing JUnit tests without Redis.
When should I use unit-test-caching?
When writing or reviewing tests for @Cacheable, @CacheEvict, @CachePut, SpEL keys, or unless/condition flags during Ship testing or Build backend hardening.
Is unit-test-caching safe to install?
It may run Bash for builds and write test files; check the Security Audits panel on this Prism page and run tests in a trusted repo only.
SKILL.md
READMESKILL.md - Unit Test Caching
# 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 1. **Configure in-memory CacheManager**: Use `ConcurrentMapCacheManager` for tests 2. **Set up test fixtures**: Mock repository and create service instance in `@BeforeEach` 3. **Verify repository call counts**: Use `times(n)` assertions to confirm cache behavior 4. **Test cache hit**: Call method twice, verify repository called once 5. **Test cache miss**: Verify repository called on each invocation 6. **Test eviction**: After `@CacheEvict`, verify repository called again on next read 7. **Test key generation**: Verify compound keys from SpEL expressions 8. **Validate conditional caching**: Test `unless` (null results) and `condition` (parameter-based) **Validation checkpoints:** - 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 ```xml <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 ```kotlin dependencies { implementation("org.springframework.boot:spring-boot-starter-cache") testImplementation("org.springframework.boot:spring-boot-starter-test") } ``` ### Testing `@Cacheable` (Cache Hit/Miss) ```java // Service @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); } } // Test 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)); // First call - hits database User firstCall = userService.getUserById(1L); // Second call - hits cache User secondCall = userService.getUserById(1L); assertThat(firstCall).isEqualTo(secondCall); verify(userRepository, times(1)).findById(1L); // Only once due to cache } @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); // No caching oc