【问题标题】:Unit Test for Redis cache in JavaJava中Redis缓存的单元测试
【发布时间】:2021-11-15 02:13:33
【问题描述】:

注意:我已经查看并尝试了一些关于 SO 的方法,例如How to test Spring's declarative caching support on Spring Data repositories?,但由于它们中的大多数都老了,我无法让它们正常工作,我需要一个具有最新库版本的解决方案。因此,如果您查看问题和帮助,我将不胜感激。

@Service
@EnableCaching
@RequiredArgsConstructor
public class DemoServiceImpl implements DemoService {

    private static final String CACHE_NAME = "demoCache";

    private final LabelRepository labelRepository;
    private final LabelTranslatableRepository translatableRepository;
    private final LanguageService languageService;


    @Override
    public LabelDTO findByUuid(UUID uuid) {
        final Label label = labelRepository.findByUuid(uuid)
                .orElseThrow(() -> new EntityNotFoundException("Not found."));
        final List<LabelTranslatable> translatableList = translatableRepository.findAllByEntityUuid(uuid);
        return new LabelDTO(Pair.of(label.getUuid(), label.getKey()), translatableList);
    }
}

我创建了以下单元测试来测试上述网络的缓存:

@EnableCaching
@ImportAutoConfiguration(classes = {
        CacheAutoConfiguration.class,
        RedisAutoConfiguration.class
})
@ExtendWith(MockitoExtension.class)
class TextLabelServiceImpl_deneme_Test {

    @Autowired
    private CacheManager cacheManager;

    @InjectMocks
    private LabelService labelService;

    @Mock
    private LabelRepository labelRepository;

    @Mock
    private LabelTranslatableRepository translatableRepository;

    @Test
    void test_Cache() {
        UUID uuid = UUID.randomUUID();
        final TextLabel textLabel = new TextLabel();
        textLabel.setId(1);
        textLabel.setKey("key1");

        TextLabelTranslatable textLabelTranslatable = new TextLabelTranslatable();
        textLabelTranslatable.setEntityUuid(uuid);
        textLabelTranslatable.setLanguage(SupportedLanguage.fr);
        textLabelTranslatable.setValue("value1");

        final List<TextLabelTranslatable> translatableList = new ArrayList<>();
        translatableList.add(textLabelTranslatable);

        when(labelRepository.findByUuid(uuid)).thenReturn(Optional.of(textLabel));
        when(translatableRepository.findAllByEntityUuid(uuid)).thenReturn(translatableList);

        TextLabelDTO result1 = labelService.findByUuid(uuid);
        TextLabelDTO result2 = labelService.findByUuid(uuid);

        assertEquals(result1, result2);
        Mockito.verify(translatableRepository, Mockito.times(1)).findAllByEntityUuid(uuid);
    }

我不确定我的测试中是否缺少部分,但在最后一行 (Mockito.verify()),它返回 2 而不是 1,这意味着缓存不起作用。但它工作正常,我认为我的测试存在问题。我应该如何完成单元测试以正确检查缓存?

【问题讨论】:

  • 还有其他人曾经为 Java 中的缓存创建单元测试吗?

标签: java spring unit-testing testing junit


【解决方案1】:

您需要使用@Cacheable 注释服务类方法。尝试遵循this tutorial 中的代码。以下测试代码按预期工作

@Import({CacheConfig.class, DemoServiceImpl.class})
@ExtendWith(SpringExtension.class)
@EnableCaching
@ImportAutoConfiguration(classes = {
    CacheAutoConfiguration.class,
    RedisAutoConfiguration.class
})
class DemoServiceImplTest {

  @MockBean
  private LabelRepository labelRepository;

  @Autowired
  private DemoServiceImpl demoService;

  @Autowired
  private CacheManager cacheManager;

  @TestConfiguration
  static class EmbeddedRedisConfiguration {

    private final RedisServer redisServer;

    public EmbeddedRedisConfiguration() {
      this.redisServer = new RedisServer();
    }

    @PostConstruct
    public void startRedis() {
      redisServer.start();
    }

    @PreDestroy
    public void stopRedis() {
      this.redisServer.stop();
    }
  }

  @Test
  void givenRedisCaching_whenFindItemById_thenItemReturnedFromCache() {
    UUID id = UUID.randomUUID();
    Label aLabel = new Label(id, "label");

    Mockito.when(labelRepository.findById(id)).thenReturn(Optional.of(aLabel));

    Label labelCacheMiss = demoService.findByUuid(id);
    Label labelCacheHit = demoService.findByUuid(id);

    Mockito.verify(labelRepository, Mockito.times(1)).findById(id);
  }
}

使用此服务类代码:

@Service
@RequiredArgsConstructor
@EnableCaching
public class DemoServiceImpl {

  public static final String CACHE_NAME = "demoCache";

  private final LabelRepository labelRepository;

  @Cacheable(value = CACHE_NAME)
  public Label findByUuid(UUID uuid) {
    return labelRepository.findById(uuid)
        .orElseThrow(() -> new EntityNotFoundException("Not found."));
  }
}

还有这个CacheConfig

@Configuration
public class CacheConfig {

  @Bean
  public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
    return (builder) -> builder
        .withCacheConfiguration(DemoServiceImpl.CACHE_NAME,
            RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)));
  }

  @Bean
  public RedisCacheConfiguration cacheConfiguration() {
    return RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(60))
        .disableCachingNullValues()
        .serializeValuesWith(
            RedisSerializationContext.SerializationPair.fromSerializer(
                new GenericJackson2JsonRedisSerializer()));
  }
}

【讨论】:

  • #1 非常感谢,正好有时间看看。首先,是的,我已经在我的服务方法中使用了@Cacheable(),但没有添加到有问题的代码中。另一方面,我不确定是否必须在单元测试中使用CacheConfig 来测试缓存。正常来说是不错的,但是我们没用过,如果我不需要测试,我就不想加了。
  • #2 ** @ImportAutoConfiguration(classes = { CacheAutoConfiguration.class, RedisAutoConfiguration.class }) 是否为您在 CacheConfig 中发布的部分进行默认设置(配置)?我认为如果我们不使用CacheConfig 类,我们可能会使用@ImportAutoConfiguration() 注释。我错了吗?
  • @Robert,ImportAutoConfiguration 的东西是由 springboot 根据你的类路径中的类自动完成的。您在此处添加它以进行测试。 CacheConfig 的东西正在自定义默认值。如果您的缓存在没有此自定义的情况下工作,那么您可以不使用它。
  • 是的,我意识到在您建议的页面上(带有 Redis 的 Spring Boot Cache),我也按照该示例进行了实施。但是,我无法执行缓存。有一个错误我仍然无法修复。你能看看Cannot test Spring Caching in Unit Test吗?
猜你喜欢
  • 1970-01-01
  • 2020-07-07
  • 2021-11-17
  • 1970-01-01
  • 2021-08-19
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
  • 2018-07-05
相关资源
最近更新 更多