在 Kotlin 和 JUnit5 中测试 Spring 引导缓存
Test Spring Boot cache in Kotlin and JUnit5
我有一个简单的存储库,它的界面是用 Kotlin 编写的,用于从 db 获取站点列表;我用 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)
}
现在我想测试一下缓存是否真的有效。作为测试的基础,我使用 How to test Spring's declarative caching support on Spring Data repositories? 但直接实施导致存储库成为代理而不是存储库的错误。所以我目前的尝试是:
@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
}
但是第一次读取后缓存为空且未填充。我的设置中缺少什么?
更新:
根据 George 的建议,我更新了测试(以及更容易模拟的代码)。我还必须在配置中为存储库添加 @Bean
,因为 Could not autowire. No beans of 'Repository' type found.
没有它。
@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>>())
}
}
现在测试甚至没有开始:
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'
更新 2:
正如乔治指出的那样,解决方案是 ( and )
@Autowired
private lateinit var repository: IRepository
你在嘲笑你的测试对象Repository repository
。这应该是 Spring 初始化的真实对象,所以它有缓存。您需要模拟您的测试对象正在调用的 JdbcTemplate
。
我不太了解 kotlin 语法,请耐心等待。您的测试应如下所示:
@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)
}
我有一个简单的存储库,它的界面是用 Kotlin 编写的,用于从 db 获取站点列表;我用 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)
}
现在我想测试一下缓存是否真的有效。作为测试的基础,我使用 How to test Spring's declarative caching support on Spring Data repositories? 但直接实施导致存储库成为代理而不是存储库的错误。所以我目前的尝试是:
@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
}
但是第一次读取后缓存为空且未填充。我的设置中缺少什么?
更新:
根据 George 的建议,我更新了测试(以及更容易模拟的代码)。我还必须在配置中为存储库添加 @Bean
,因为 Could not autowire. No beans of 'Repository' type found.
没有它。
@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>>())
}
}
现在测试甚至没有开始:
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'
更新 2:
正如乔治指出的那样,解决方案是 ( and )
@Autowired
private lateinit var repository: IRepository
你在嘲笑你的测试对象Repository repository
。这应该是 Spring 初始化的真实对象,所以它有缓存。您需要模拟您的测试对象正在调用的 JdbcTemplate
。
我不太了解 kotlin 语法,请耐心等待。您的测试应如下所示:
@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)
}