【发布时间】:2015-10-15 12:51:32
【问题描述】:
在记录返回字段值的期望时,我希望返回的值是调用实际方法时的字段值(引用的值),而不是期望时的字段值被记录下来了。
这是正在测试的类(实际上是其中 2 个):
public class ListObservingCache<T> extends ObservingCache {
public ListObservingCache(Supplier<List<T>> syncFunc, int intervalMillis) {
super(syncFunc, intervalMillis);
}
@SuppressWarnings("unchecked")
@Override
public List<T> getItems() {
return items != null ? Collections.unmodifiableList((List<T>) items) : null;
}
}
public abstract class ObservingCache {
private static final int DEFAULT_CACHE_REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes
private static int DEFAULT_CACHE_INITIAL_DELAY = 10 * 60 * 1000; // 10 minutes
private static final int DEFAULT_THREAD_POOL_SIZE = 5;
private static ScheduledExecutorService executor;
protected Object items;
protected ObservingCache(Supplier<? extends Object> syncFunc) {
this(syncFunc, DEFAULT_CACHE_REFRESH_INTERVAL);
}
protected ObservingCache(Supplier<? extends Object> syncFunc, int intervalMillis) {
if (executor == null || executor.isShutdown()) {
executor = Executors.newScheduledThreadPool(DEFAULT_THREAD_POOL_SIZE);
}
Runnable task = () -> {
Object result = syncFunc.get();
if (result != null) {
items = result;
}
};
task.run(); // First run is blocking (saves a lot of trouble later).
executor.scheduleAtFixedRate(task, DEFAULT_CACHE_INITIAL_DELAY, intervalMillis, TimeUnit.MILLISECONDS);
}
public abstract Object getItems();
}
这是我的测试类:
public class ListObservingCacheTest {
List<Integer> provList; // <-- The field I wish to use instead of the "willReturnList()" method
@Mocked
DummyTask mockTask;
@BeforeClass
public static void setupClass() {
ObservingCache.DEFAULT_CACHE_INITIAL_DELAY = 100;
}
@AfterClass
public static void tearDownClass() {
ExecutorService toShutDown = (ExecutorService) getField(ObservingCache.class, "executor");
toShutDown.shutdown();
}
@Before
public void setUp() {
mockTask = new DummyTask(); // Empty list
}
@Test
public void testBasic() throws Exception {
willReturnList(Arrays.asList(1, 2));
ListObservingCache<Integer> obsCache = new ListObservingCache<Integer>(() -> mockTask.acquireList(), 300);
assertEquals(Arrays.asList(1, 2), obsCache.getItems());
willReturnList(Arrays.asList(3, 4, 5));
assertEquals(Arrays.asList(1, 2), obsCache.getItems()); // ObservingCache should still returns the former list because its interval hasn't passed yet
Thread.sleep(300);
assertEquals(Arrays.asList(3, 4, 5), obsCache.getItems()); // ObservingCache should now return the "new" list, as its interval has passed and the task has been executed again
}
/**
* Instructs the mock task to return the specified list when its
* acquireList() method is called
*/
private void willReturnList(List<Integer> list) {
new Expectations() {{ mockTask.acquireList(); result = list; }};
}
/**
* Simulates an ObservingCache "real-life" task. Should never really be
* called (because it's mocked).
*/
class DummyTask {
private List<Integer> list;
public List<Integer> acquireList() {
return list;
}
}
}
这个测试通过了,但我想要一种更优雅的方式来设置acquireList() 方法的返回值的期望值,因为一旦我拥有多个这些在同一个班级。
我正在寻找类似于 mockito-syntax 命令的东西:
when(mockTask.acquireList()).thenReturn(provList);
这应该始终返回 provList 字段的当前值(与记录期望时的值相反)。
编辑: 在浏览了文档之后,我想出了一个使用委托的解决方案:
new Expectations() {{
mockTask.acquireList();
result = new Delegate<List<Integer>>() {
List<Integer> delegate() {
return provList; // The private field
}
};
}};
这种方法有两个问题:
1. 不优雅
2.List<Integer> delegate()方法导致编译时警告:
new Delegate>(){} 类型的方法 delegate() 永远不会 本地使用
因此,仍在寻找其他解决方案
【问题讨论】:
-
你应该描述你想要解决的实际问题,而不是描述一个解决方案。示例测试没有帮助,因为它所做的只是测试模拟本身。
-
我确实描述了这个问题 - 我需要找到一种优雅/有效的方法来使测试通过。提出的 3 个解决方案应该为任何试图回答的人指明方向。示例测试也是如此。我知道它测试模拟的事实。我这样写是为了尽可能以最简单的方式呈现问题。
-
就目前的情况而言,如果您仔细想想,这个问题甚至没有意义。测试如何知道 when 设置可变字段,以便模拟方法的每次调用都返回所需的字段值?假设一个模拟 API 可以支持它,我只是看不出真实世界的测试如何使用这样的功能。
-
模拟 API(您看似编写的)确实通过委托支持它,正如我在问题中所描述的那样。我编写的测试是我需要为真实世界的应用程序编写的真实测试的更简单版本。如果我认为有必要,我会指定真正的要求。
-
当然,使用
Delegate允许测试返回该字段的当前值,但是谁将该字段设置为下一个值,何时设置?那是我没有得到的部分,只有在描述了要解决的实际问题后才能变得清晰。
标签: java unit-testing mocking jmockit