在Kotlin和JUnit5中测试Spring Boot缓存

问题描述

我有一个简单的存储库,它是用Kotlin编写的接口,用于从db获取站点列表;我用Spring缓存来缓存响应:@H_502_1@

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?,但是直接实现导致存储库是代理而不是存储库的错误。所以我目前的尝试是:@H_502_1@

@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
  }

但是高速缓存为空,并且在首次读取后未填充。我的设置中缺少什么?@H_502_1@

更新:@H_502_1@

根据乔治的建议,我更新了测试(并提供了用于简化模拟的代码)。我还必须为配置中的存储库添加@Bean,因为没有Could not autowire. No beans of 'Repository' type found.。@H_502_1@

  @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>>())
  }
}

现在测试甚至无法开始:@H_502_1@

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:@H_502_1@

正如乔治所指出的,解决方案是(https://stackoverflow.com/a/44911329/492882https://stackoverflow.com/a/44911329/492882)@H_502_1@

  @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)
  }