如何测试Guava Cache只调用一次方法

How to test that a method is called by Guava Cache only once

我正在尝试测试我创建的缓存是否正常工作,对方法的重复调用实际上并没有被多次调用,而是从我的缓存中加载的。

我正在使用 Google Guava 的 LoadingCache 来完成这个。

所以我天真的方法是创建一个间谍,并验证该方法是否被调用过一次。然后我发现因为 spy() 是一个装饰器,所以我只能查看是否在该对象上调用了该方法。由于该方法被 LoadingCache 调用,我的间谍无法验证它。

如何最好地测试我的缓存是否被正确使用?

(注意:我可以创建一个 LoadingCache 依赖项,并检查是否调用了正确的方法,但是我不知道我的缓存是否在工作。也许哈希值是以我没有预料到的方式计算的, 所以其实每次都是在调用这个方法,想看看实际效果)

private final LoadingCache<TaskDetails, byte[]> cache;

    ...

    cache = CacheBuilder.newBuilder()
                .maximumSize(cacheSize)
                .expireAfterAccess(Duration.ofMinutes(cacheDurationInMinutes))
                .build(CacheLoader.from(this::doTask));
public byte[] taskCaller(...) {
    ...
    return cache.getUnchecked();
}
public byte[] doTask(...) {
    if(something.doSomething() ... ) {
    ...
}

第一次测试尝试无效,Mockito 说:需要但未调用(该方法“从未被调用”,因为间谍不知道调用它的缓存)

@Test
public void test() {
    // given
    TaskService spy = spy(
        new TaskService(...)
    );

    // when
    for (int i = 0; i < 3; i++) {
        spy.taskCaller(...);
    }

    // then
    //if caching is working, we should only do the real work once
    verify(spy, times(1)).doTask(any(TaskDetails.class));
}

第二次尝试:方法中的第一个语句出现 NullPointerException (something.doSomething()),我不完全确定原因,但我确信与之前的原因相同

@Test
public void test() {
    // given
    TaskService spy = spy(
        new TaskService(...)
    );
    final int[] counter = {0};
    when(spy.doTask(any())).thenAnswer(invocation -> {
        counter[0]++;
        return new byte[]{0,0,0,0};
    });

    // when
    int run = 3;
    for (int i = 0; i < run; i++) {
        spy.taskCaller(...);
    }

    // then
    //if caching is working, we should only do the real work once
    assertThat(run).isEqualTo(counter[0]);
    verify(spy, times(1)).doTask(any(TaskDetails.class));
}

两个想法:

  1. Enable cache stats recording, interact with the cache, get the cache stats, then verify that the load count(可能还有其他统计数据)是您所期望的。
  2. 将您的 CacheLoader 单独命名为 class 而不是方法引用,将其实例传递给 CacheBuilder.build(),并安排该实例成为您的模拟测试。然后,您可以在模拟加载器上验证方法调用计数和参数。

I'm using LoadingCache from Google Guava to accomplish this.

这就是重点。如果您足够信任 Guava,为什么不相信它的测试有效?

缓存是你写的吗?不,你没有。然后不要将其作为单元测试的一部分进行测试:仅作为集成测试的一部分,与整个应用程序的其余部分一起进行测试。

只需测试在调用缓存使用方法时调用缓存加载方法即可。

如果要测试是否有缓存,对通用方法执行两次调用,并验证只调用一次缓存加载方法。