我有一个递归过程,必须根据输出运行多次。
refreshInBatches()
方法在数据库中运行查询并返回 nodeId
。停止条件是:每当我到达结束节点(假设它有 id = 1)或我到达之前访问过的节点。
为了检查后者,我创建了一个 Set 并继续调用该方法以批量刷新我添加了一个 do-while 循环。 notEndNode()
如果没有达到停止条件则返回true
,否则返回false
。
public Mono<MyClass> refresh(String databaseName, Long nodeId, Integer batchSize, Set<Long> visitedNodes) {
Mono<MyClass> result = null;
do {
addNodeToSet(visitedNodes, result);
result = refreshInBatches(databaseName, nodeId, batchSize);
} while (notEndNode(nodeId, result.block(), visitedNodes));
return result;
}
我正试图找到一种好的方法来为我刚刚解释的这种行为添加单元测试。我想模拟数据库响应,因为我只想测试调用数据库的次数(通过
refresh()
方法)。所以我创建了一个包含 5 个元素的模拟响应列表,最后一个元素是结束节点(这意味着程序应该在那之后停止)。
模拟从
refreshInBatches()
开始,它总是应该返回 response
对象。模拟对象 node
应该从 response
返回,我在列表中添加了一个迭代器,这样我们就可以在尝试获取 nodeId
时获取下一个值。
上面的测试可以在下面找到。但它没有按我预期的方式工作。当我尝试调试应用程序时,第一次调用 refreshInBatches()
时,我得到了预期的 10L
。但是,如果我继续迭代循环,将第二次调用refreshInBatches()
,但又返回10L
,而不是iterator.next()
。结果 dbRepository.refreshInBatches()
只被调用了 2 次而不是 5 次。
@InjectMocks
DbRepository dbRepository = Mockito.spy(new DbRepository(dbClient, null));
MyClass response = Mockito.spy(new MyClass());
@Mock
Node node = Mockito.mock(Node.class);
@Test
public void testRefreshDBShouldCallTheRefreshMethodFiveTimes() {
List<Value> setIds = Arrays.asList(Values.value(10L), Values.value(20L), Values.value(50L), Values.value(60L), Values.value(1L));
Iterator<Value> iterator = setIds.iterator();
Mockito.doReturn(iterator.next()).when(node).get(anyString());
Mockito.doReturn(node).when(response).getNode();
Mockito.doReturn(Mono.just(response)).when(dbRepository).refreshInBatches(anyString(), anyLong(), anyInt());
dbRepository.refresh("dataBase", -1L, 10000, new HashSet<>());
verify(dbRepository, times(setIds.size())).refreshInBatches(anyString(), anyLong(), anyInt());
}
我认为我的代码存在一些设计缺陷,但我想了解如何使用模拟响应测试递归调用。迭代器不应该在第二次调用
dbRepository.refreshInBatches()
时移动到下一个元素吗?或者至少在其中一项检查被调用时:
private void addNodeToSet(Set<Long> visited, Mono<MyClass> projection) {
if (ObjectUtils.isNotEmpty(projection) && ObjectUtils.isNotEmpty(projection.block())) {
visited.add(projection.block().getNode().get("id").asLong());
}
}
private static boolean notEndNode(Long nodeId, MyClass projection, Set<Long> visited) {
return projection != null &&
ObjectUtils.isNotEmpty(projection.getNode()) &&
!nodeId.equals(projection.getNode().get("id").asLong()) &&
!visited.contains(projection.getNode().get("id").asLong());
}