测试 SpringBoot @Cacheable 功能

问题描述 投票:0回答:1

我已经在 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 均失败:

  1. OPTION-1 对测试 1 进行了抱怨,因为断言在 DatabaseAdatper 上发现了 3 个命中,预期为 1 个。似乎 SpringBoot @Cacheable 不起作用。也许 SpringBoot Context 没有运行。测试 2 完美运行
  2. OPTION-2 未启动。抱怨缺少 AttributePort bean。似乎自动装配无法正确找到实现

那么,我缺少什么?

根据这篇文章 OPTION-2 必须使用接口而不是 SpringBoot 的实现来声明变量才能正确自动装配。但它不起作用。 OPTION-1 似乎 @Cacheable 没有被初始化

spring-boot caching junit5
1个回答
0
投票

选项 1 不起作用,因为如果您使用

@InjectMocks
,您将让 Mockito 创建
AttributeDatabaseAdapter
的实例,该实例不会是 Spring bean(因此
@Cacheable
将被忽略)。

选项 2 是正确的解决方案。但是,现在它不起作用,因为在测试中没有任何地方告诉 Spring 容器包含

AttributeDatabaseAdapter
。最简单的方法是在配置上扩展
@Import
注释。例如:

@EnableCaching
@Configuration
@Import({AttributeMapperImpl.class, AttrinbuteDatabaseAdapter.class}) // Change this
public static class CachingTestConfig {
    // ...

}

我还有一些其他建议:

  1. 不要对
    @Configuration
    使用
    CachingTestConfig
    注释,最好使用
    @TestConfiguration
  2. 不要对
    @Mock
    使用
    AttributeRepository
    注释,然后必须使用
    @Bean
    将其包含在您的配置中,如果您使用
    @MockBean
    对其进行注释,然后只需删除您的配置中的
    repository()
    方法,那么会更干净。
    CachingTestConfig
  3. 您为
    AttributeMapper
    创建的间谍可能不会做太多事情,因为您已经通过
    @Import
    导入它,并且 Spring 将注入该实例而不是您的间谍。
© www.soinside.com 2019 - 2024. All rights reserved.