I have a simple repository and it's interface written in Kotlin to get a list of sites from db; and I cache response with Spring cache:
interface IRepository {
fun sites(): List<String>
}
@Repository
class Repository(private val jdbcTemplate: NamedParameterJdbcTemplate) : IRepository {
private val sites = "SELECT DISTINCT siteId FROM sites"
@Cacheable(value = ["sites"], key = "sites")
override fun sites(): List<String> = jdbcTemplate.jdbcTemplate.queryForList(sites, String::class.java)
}
Now I want to test that caching is actually working. As base for test I used How to test Spring's declarative caching support on Spring Data repositories? but direct implementation resulted in error of repository being a proxy and not repository. So my current attempt is:
@ContextConfiguration
@ExtendWith(SpringExtension::class)
class RepositoryCacheTests {
@MockBean
private lateinit var repository: Repository
@Autowired
private lateinit var cache: CacheManager
@EnableCaching
@TestConfiguration
class CachingTestConfig {
@Bean
fun cacheManager(): CacheManager = ConcurrentMapCacheManager("sites")
}
@Test
fun `Sites is cached after first read`() {
// Arrange
whenever(repository.sites()).thenReturn(listOf(site, anotherSite))
repository.sites()
// Assert
assertThat(cache.getCache("sites")?.get("sites")).isNotNull
}
But cache is empty and not populated after first read. What am I missing in my setup?
Update:
Using George's suggestion I updated the test (and code for easier mocking). I also had to add @Bean
for repository in configuration because of Could not autowire. No beans of 'Repository' type found.
without it.
@Cacheable(value = ["sites"], key = "'sites'")
override fun sites(): List<String> = jdbcTemplate.query(sites) { rs, _ -> rs.getString("siteId") }
@ContextConfiguration
@ExtendWith(SpringExtension::class)
class RepositoryCacheTests {
@MockBean
private lateinit var jdbcTemplate: NamedParameterJdbcTemplate
@Autowired
private lateinit var repository: Repository
@Autowired
private lateinit var cache: CacheManager
@EnableCaching
@TestConfiguration
class CachingTestConfig {
@Bean
fun testRepository(jdbcTemplate: NamedParameterJdbcTemplate): Repository = Repository(jdbcTemplate)
@Bean
fun cacheManager(): CacheManager = ConcurrentMapCacheManager("sites")
}
@Test
fun `Sites is cached after first read`() {
whenever(jdbcTemplate.query(any(), any<RowMapper<String>>())).thenReturn(listOf(site, anotherSite))
repository.sites()
assertThat(cache.getCache("sites")?.get("sites")).isNotNull
repository.sites()
verify(jdbcTemplate, times(1)).query(any(), any<RowMapper<String>>())
}
}
Now the test does not even start:
Error creating bean with name 'RepositoryCacheTests': Unsatisfied dependency expressed through field 'repository'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'testRepository' is expected to be of type 'Repository' but was actually of type 'com.sun.proxy.$Proxy52'
Update 2:
As George points, solution is (https://stackoverflow.com/a/44911329/492882 and https://stackoverflow.com/a/44911329/492882)
@Autowired
private lateinit var repository: IRepository
You are mocking your test subject Repository repository
. This should be the real object initialized by Spring so it has caching. You need to mock the JdbcTemplate
that your test subject is calling.
I don't really know kotlin syntax, so bear with me. Here's how your test should look like:
@ContextConfiguration
@ExtendWith(SpringExtension::class)
class RepositoryCacheTests {
@MockBean
private lateinit jdbcTemplate: NamedParameterJdbcTemplate
@Autowired
private lateinit var repository: IRepository
@Autowired
private lateinit var cache: CacheManager
@EnableCaching
@TestConfiguration
class CachingTestConfig {
@Bean
fun cacheManager(): CacheManager = ConcurrentMapCacheManager("sites")
}
@Test
fun `Sites is cached after first read`() {
// Arrange
whenever(jdbcTemplate.queryForList(any(), String::class.java)).thenReturn(listOf(site, anotherSite))
repository.sites()
// Assert
assertThat(cache.getCache("sites")?.get("sites")).isNotNull
//Execute again to test cache.
repository.sites()
//JdbcTemplate should have been called once.
verify(jdbcTemplate, times(1)).queryForList(any(), String::class.java)
}