我已经在 Spring Boot 应用程序上实现了一些 DDBB 访问的缓存。 到目前为止它一直在工作,但我们希望确保它在未来不会被禁用,所以我想添加一个 JUnit 测试来在每个编译轮上断言它。
我无法让它发挥作用。我已按照其他 SO 线程的说明进行操作,但没有成功 在 Kotlin 和 JUnit5 中测试 Spring Boot 缓存
我们与此事相关的堆栈是 SpringBoot 2.7、JUnit 5
启用@Cacheable的类是这样的
@Component
public class AttributeDatabaseAdapter implements AttributePort {
final AttributeRepository repository;
final AttributeMapper mapper;
public AttributeDatabaseAdapter(AttributeRepository repository, AttributeMapper mapper) {
this.repository = repository;
this.mapper = mapper;
}
@Override
@Cacheable(value = CacheConfig.CACHE_ATTRIBUTE, key="{ #attributeName, #userId }")
public Attribute getActiveAttributeByName(String attributeName, Integer userId) {
List<AttributeEntity> listAttribute = repository.findAll(AttributeSpecifications.getFilterFromSelector(attributeName));
if (!listAttribute.isEmpty()) {
if (listAttribute.size() == 1) {
return mapper.toDomainObject(listAttribute.get(0));
} else {
throw new NonUniqueResultException("A unique result was expected but more elements were found.");
}
}
return new Attribute();
}
}
现在为了测试,我已经按照这个 Baeldung 教程构建了这个 JUnit 文件,并用这个 StackOverflow 帖子
进行了调味@ContextConfiguration
@ExtendWith(SpringExtension.class)
public class AttributeDatabaseAdapterTest {
@Mock
private static AttributeRepository repositoryMock;
@Spy
private static AttributeMapper attributeMapper = new AttributeMapperImpl();
@EnableCaching
@Configuration
@Import({AttributeMapperImpl.class})
public static class CachingTestConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager(CacheConfig.CACHE_ATTRIBUTE);
}
@Bean
public AttributeRepository repository() {
return repositoryMock;
}
}
// OPTION 1
// @InjectMocks private AttributeDatabaseAdapter databaseAdapter;
// OPTION 2
@Autowired private AttributePort databaseAdapter;
@BeforeEach
void setUp() {
reset(repositoryMock);
}
private final static AttributeEntity ATTR1 = AttributeEntity.builder().techId(1).build();
private final static AttributeEntity ATTR2 = AttributeEntity.builder().techId(2).build();
@Test
@DisplayName("Calling DatabaseAdapter two times makes one call to repository")
void multiple_calls_must_hit_databaseadapter_only_once() {
when(repositoryMock.findAll(any(Specification.class))).thenReturn(Arrays.asList(ATTR1));
// Mapper makes new instances, so can't compare objects. Must compare values
assertEquals(ATTR1.getTechId(), databaseAdapter.getActiveAttributeByName("TEST", 1).getTechId());
assertEquals(ATTR1.getTechId(), databaseAdapter.getActiveAttributeByName("TEST", 1).getTechId());
assertEquals(ATTR1.getTechId(), databaseAdapter.getActiveAttributeByName("TEST", 1).getTechId());
verify(repositoryMock, times(1)).findAll(any(Specification.class)); // Fails on OPTION 1
}
@Test
@DisplayName("Assert Repository must return one single result")
void filtering_Must_Return_single_Result() {
when(repositoryMock.findAll(any(Specification.class))).thenReturn(Arrays.asList(ATTR1, ATTR2));
assertThrows(NonUniqueResultException.class, () -> databaseAdapter.getActiveAttributeByName("TEST", 1));
}
}
选项 1 和选项 2 均失败:
那么,我缺少什么?
根据这篇文章 OPTION-2 必须使用接口而不是 SpringBoot 的实现来声明变量才能正确自动装配。但它不起作用。 OPTION-1 似乎 @Cacheable 没有被初始化
选项 1 不起作用,因为如果您使用
@InjectMocks
,您将让 Mockito 创建 AttributeDatabaseAdapter
的实例,该实例不会是 Spring bean(因此 @Cacheable
将被忽略)。
选项 2 是正确的解决方案。但是,现在它不起作用,因为在测试中没有任何地方告诉 Spring 容器包含
AttributeDatabaseAdapter
。最简单的方法是在配置上扩展 @Import
注释。例如:
@EnableCaching
@Configuration
@Import({AttributeMapperImpl.class, AttrinbuteDatabaseAdapter.class}) // Change this
public static class CachingTestConfig {
// ...
}
我还有一些其他建议:
@Configuration
使用 CachingTestConfig
注释,最好使用 @TestConfiguration
。@Mock
使用 AttributeRepository
注释,然后必须使用 @Bean
将其包含在您的配置中,如果您使用 @MockBean
对其进行注释,然后只需删除您的配置中的 repository()
方法,那么会更干净。 CachingTestConfig
。AttributeMapper
创建的间谍可能不会做太多事情,因为您已经通过 @Import
导入它,并且 Spring 将注入该实例而不是您的间谍。