I have my cache config as below;
@Configuration
public class CacheConfiguration {
@Bean
public CacheManager cacheManager(Ticker ticker) {
CaffeineCache bookCache = buildCache("books", ticker, 30);
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList(bookCache));
return cacheManager;
}
private CaffeineCache buildCache(String name, Ticker ticker, int minutesToExpire) {
return new CaffeineCache(name, Caffeine.newBuilder()
.expireAfterWrite(minutesToExpire, TimeUnit.MINUTES)
.maximumSize(100)
.ticker(ticker)
.build());
}
@Bean
public Ticker ticker() {
return Ticker.systemTicker();
}
}
And the service I want to test:
@Service
public class TestServiceImpl implements TestService {
private final BookRepository bookRepository; // interface
@Autowired
public TestServiceImpl(final BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@Override
public Book getByIsbn(String isbn) {
return bookRepository.getByIsbn(isbn);
}
}
The required method in repository is annotated with @Cacheable("books")
.
@Override
@Cacheable("books")
public Book getByIsbn(String isbn) {
LOGGER.info("Fetching Book...");
simulateSlowService(); // Wait for 5 secs
return new Book(isbn, "Some book");
}
I need to write a test showing the caching works. So I created another ticker
bean in test to override the one existing in CacheConfiguration
. The code;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestServiceTests {
private static final String BOOK_ISBN = "isbn-8442";
@SpyBean
private BookRepository bookRepository;
@Autowired
private TestService testService;
@Configuration
@Import(SpringBootCacheApplication.class)
public static class TestConfiguration {
//testCompile('com.google.guava:guava-testlib:23.6-jre')
static FakeTicker fakeTicker = new FakeTicker();
@Bean
public Ticker ticker() {
return fakeTicker::read;
}
}
@Before
public void setUp() {
Book book = fakeBook();
doReturn(book)
.when(bookRepository)
.getByIsbn(BOOK_ISBN);
}
private Book fakeBook() {
return new Book(BOOK_ISBN, "Mock Book");
}
@Test
public void shouldUseCache() {
// Start At 0 Minutes
testService.getByIsbn(BOOK_ISBN);
verify(bookRepository, times(1)).getByIsbn(BOOK_ISBN);
// After 5 minutes from start, it should use cached object
TestConfiguration.fakeTicker.advance(5, TimeUnit.MINUTES);
testService.getByIsbn(BOOK_ISBN);
verify(bookRepository, times(1)).getByIsbn(BOOK_ISBN); // FAILS HERE
// After 35 Minutes from start, it should call the method again
TestConfiguration.fakeTicker.advance(30, TimeUnit.MINUTES);
testService.getByIsbn(BOOK_ISBN);
verify(bookRepository, times(2)).getByIsbn(BOOK_ISBN);
}
}
But it fails at the line marked with //FAILS HERE
with message;
org.mockito.exceptions.verification.TooManyActualInvocations:
simpleBookRepository.getByIsbn("isbn-8442");
Wanted 1 time:
-> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
But was 2 times. Undesired invocation:
-> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)`
Why it fails? Shouldn't it use cache? Or my test is wrong?
Any help or pointers are greatly appreciated! :)