如何测试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));
}
两个想法:
- Enable cache stats recording, interact with the cache, get the cache stats, then verify that the load count(可能还有其他统计数据)是您所期望的。
- 将您的
CacheLoader
单独命名为 class 而不是方法引用,将其实例传递给 CacheBuilder.build()
,并安排该实例成为您的模拟测试。然后,您可以在模拟加载器上验证方法调用计数和参数。
I'm using LoadingCache from Google Guava to accomplish this.
这就是重点。如果您足够信任 Guava,为什么不相信它的测试有效?
缓存是你写的吗?不,你没有。然后不要将其作为单元测试的一部分进行测试:仅作为集成测试的一部分,与整个应用程序的其余部分一起进行测试。
只需测试在调用缓存使用方法时调用缓存加载方法即可。
如果要测试是否有缓存,对通用方法执行两次调用,并验证只调用一次缓存加载方法。
我正在尝试测试我创建的缓存是否正常工作,对方法的重复调用实际上并没有被多次调用,而是从我的缓存中加载的。
我正在使用 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));
}
两个想法:
- Enable cache stats recording, interact with the cache, get the cache stats, then verify that the load count(可能还有其他统计数据)是您所期望的。
- 将您的
CacheLoader
单独命名为 class 而不是方法引用,将其实例传递给CacheBuilder.build()
,并安排该实例成为您的模拟测试。然后,您可以在模拟加载器上验证方法调用计数和参数。
I'm using LoadingCache from Google Guava to accomplish this.
这就是重点。如果您足够信任 Guava,为什么不相信它的测试有效?
缓存是你写的吗?不,你没有。然后不要将其作为单元测试的一部分进行测试:仅作为集成测试的一部分,与整个应用程序的其余部分一起进行测试。
只需测试在调用缓存使用方法时调用缓存加载方法即可。
如果要测试是否有缓存,对通用方法执行两次调用,并验证只调用一次缓存加载方法。