You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何Mock MongoDatabase的listCollectionNames方法编写单元测试

问题背景

现有一个引入spring-data-mongo依赖的Spring Boot项目,项目运行状态正常。其中一个服务的功能为:对指定Mongo数据库执行listCollections操作,遍历清空该库下所有集合的内容,对应业务代码如下:

public void clearContentsOfAllCollections() {
  MongoDatabase db = this.mongoTemplate.getMongoDbFactory().getDb("any-db-name");
  LOGGER.info("=================================================================");
  LOGGER.info("Clearing collections in DB - {}", db.getName());
  MongoIterable<String> collectionNames = db.listCollectionNames();
  for (final String collectionName : collectionNames) {
    LOGGER.info("Clearing collection - {}", collectionName);
    db.getCollection(collectionName).deleteMany(new Document());
  }
  LOGGER.info("Successfully cleared DB - {}", db.getName());
  LOGGER.info("=================================================================");
}

编写该方法的单元测试时,已完成部分Mock逻辑编写,测试代码如下:

@Test
public void dropDBTest() {
  SimpleMongoClientDbFactory simpleMongoClientDbFactory = Mockito.mock(SimpleMongoClientDbFactory.class);
  MongoDatabase mongoDatabase = Mockito.mock(MongoDatabase.class);
  Mockito.when(mongoTemplate.getMongoDbFactory()).thenReturn(simpleMongoClientDbFactory);
  Mockito.when(simpleMongoClientDbFactory.getDb("db-name"))
    .thenReturn(mongoDatabase);

  // How do I convert this array list into a Mongo iterable 
   List<String> collectionList = Arrays.asList("collection-1", "collection-2");
}
待解决问题
  • 无法将构造好的普通字符串List转换为MongoIterable<String>类型,无法Mockcom.mongodb.client包下database.listCollectionNames()方法的返回值
  • MongoIterable的iterator()方法返回类型为MongoCursor,不清楚该部分逻辑如何处理才能正确完成单元测试
解决方案

不需要手动实现MongoIterable、MongoCursor接口的全部方法,直接通过Mockito分层Mock即可,利用普通List的迭代器驱动Mock游标的遍历行为,写法兼容所有主流版本的Mongo Java驱动和Mockito。
注意原有测试代码存在一个容易忽略的问题:业务代码调用getDb传入的参数是any-db-name,Mock时写的参数是db-name,参数不匹配会导致Mock规则不生效,需要统一参数,或者直接用Mockito.anyString()做宽松匹配。
完整可运行的测试代码如下:

@Test
public void dropDBTest() {
  // 初始化基础Mock对象
  SimpleMongoClientDbFactory simpleMongoClientDbFactory = Mockito.mock(SimpleMongoClientDbFactory.class);
  MongoDatabase mongoDatabase = Mockito.mock(MongoDatabase.class);
  MongoCollection<Document> coll1 = Mockito.mock(MongoCollection.class);
  MongoCollection<Document> coll2 = Mockito.mock(MongoCollection.class);

  // 构造测试用集合名列表
  List<String> collectionList = Arrays.asList("collection-1", "collection-2");
  Iterator<String> listIterator = collectionList.iterator();

  // Mock MongoCursor,复用普通List的迭代器逻辑
  MongoCursor<String> mockCursor = Mockito.mock(MongoCursor.class);
  Mockito.when(mockCursor.hasNext()).thenAnswer(inv -> listIterator.hasNext());
  Mockito.when(mockCursor.next()).thenAnswer(inv -> listIterator.next());

  // Mock MongoIterable,绑定游标返回
  MongoIterable<String> mockCollNames = Mockito.mock(MongoIterable.class);
  Mockito.when(mockCollNames.iterator()).thenReturn(mockCursor);

  // 绑定所有Mock返回规则
  Mockito.when(mongoTemplate.getMongoDbFactory()).thenReturn(simpleMongoClientDbFactory);
  Mockito.when(simpleMongoClientDbFactory.getDb("any-db-name")).thenReturn(mongoDatabase);
  Mockito.when(mongoDatabase.getName()).thenReturn("any-db-name");
  Mockito.when(mongoDatabase.listCollectionNames()).thenReturn(mockCollNames);
  Mockito.when(mongoDatabase.getCollection("collection-1")).thenReturn(coll1);
  Mockito.when(mongoDatabase.getCollection("collection-2")).thenReturn(coll2);

  // 执行被测方法
  clearService.clearContentsOfAllCollections();

  // 验证执行结果:两个集合都执行了deleteMany操作
  Mockito.verify(coll1).deleteMany(Mockito.any(Document.class));
  Mockito.verify(coll2).deleteMany(Mockito.any(Document.class));
}

如果使用的是Mongo Java Driver 4.x版本,也可以直接用驱动自带的MongoIterable实现类简化代码,不需要手动Mock游标:

// 4.x版本可直接用该方式构造MongoIterable
MongoIterable<String> mockCollNames = ListMongoIterable.of(collectionList);

内容的提问来源于stack exchange,提问作者Anirudh Ramesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 15:48:15