ssg*_*gao 10 java mockito guava
我要测试的课程:
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class Subject {
private CacheLoader<String, String> cacheLoader = new CacheLoader<String, String>() {
@Override
public String load(String key)
throws Exception {
return retrieveValue(key);
}
};
private LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.build(cacheLoader);
public String getValue(String key) {
return cache.getUnchecked(key);
}
String retrieveValue(String key) {
System.out.println("I should not be called!");
return "bad";
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的测试用例
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class SubjectTest {
String good = "good";
@Spy
@InjectMocks
private Subject subject;
@Test
public void test() {
doReturn(good).when(subject).retrieveValue(anyString());
assertEquals(good, subject.getValue("a"));
}
}
Run Code Online (Sandbox Code Playgroud)
我有
org.junit.ComparisonFailure:
Expected :good
Actual :bad
Run Code Online (Sandbox Code Playgroud)
Mark Peters在诊断和解释根本原因方面做得很好.我可以想到几个解决方法:
将缓存(重新)初始化移动到单独的方法中.
通过new CacheLoader从间谍内部调用,创建匿名内部类,并引用间谍作为父实例.根据您测试的实际系统,您还可以从构造函数路径中创建缓存,尤其是在涉及任何繁重的初始化或加载时.
public class Subject {
public Subject() {
initializeCache();
}
private LoadingCache<String, String> cache;
@VisibleForTesting
void initializeCache() {
cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return retrieveValue(key);
}
});
}
/* ... */
}
@Test
public void test() {
subject.initializeCache();
doReturn(good).when(subject).retrieveValue(anyString());
assertEquals(good, subject.getValue("a"));
}
Run Code Online (Sandbox Code Playgroud)进行手动覆盖.
您遇到麻烦的根本原因是间谍实例与原始实例不同.通过覆盖测试中的单个实例,您可以在不处理不匹配的情况下更改行为.
@Test
public void test() {
Subject subject = new Subject() {
@Override public String getValue() { return "good"; }
}
}
Run Code Online (Sandbox Code Playgroud)重构.
虽然您可以使用完整的DI,但您可以只为值函数添加测试接缝:
public class Subject {
private CacheLoader<String, String> cacheLoader = new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return valueRetriever.apply(key);
}
};
private LoadingCache<String, String> cache =
CacheBuilder.newBuilder().build(cacheLoader);
Function<String, String> valueRetriever = new Function<String, String>() {
@Override
public String apply(String t) {
System.out.println("I should not be called!");
return "bad";
}
};
public String getValue(String key) {
return cache.getUnchecked(key);
}
}
@Test
public void test() {
subject = new Subject();
subject.valueRetriever = (x -> good);
assertEquals(good, subject.getValue("a"));
}
Run Code Online (Sandbox Code Playgroud)
当然,根据您的需要,valueRetriever可以是一个完全独立的类,或者您可以接受整个CacheLoader作为参数.
这归结为间谍的实施.根据文档,Spy是作为真实实例的副本创建的:
Mockito 不会将调用委托给传递的实例,而是实际创建它的副本.因此,如果您保留真实实例并与之交互,请不要指望间谍知道这些交互及其对实际实例状态的影响.其必然结果是,当unstubbed方法被调用的间谍,但不是真正的实例,你不会看到真正的实例的任何影响.
这似乎是一个浅薄的副本.因此,就我的调试显示而言,CacheLoader副本和原始对象之间共享,但它对其封闭对象的引用是原始对象,而不是间谍.因此,真实retrieveValue被称为而不是被模拟的真实.
我不确定最好的解决方法是什么.这个具体示例的一种方法是反转CacheLoader依赖(即将其传递给内部Subject而不是在Subject内部定义),并模拟而不是Subject.
| 归档时间: |
|
| 查看次数: |
4787 次 |
| 最近记录: |