如何用Mockito模拟Mono.from(commandResult).block()?MongoDB响应式单元测试
解决方法
要模拟Mono.from(commandResult).block()的行为,核心是让db.runCommand(bsonDocument)返回预定义的Publisher<Document>(比如Mono),这样Mono.from()会直接复用这个流,block()就能得到预期结果。同时因为目标方法是私有方法,你可以通过测试调用它的公共方法,或者用反射直接调用私有方法完成测试。
步骤1:确保被测类依赖可注入
先让被测类通过构造函数注入MongoClient,这样才能用Mockito mock这个依赖:
public class MongoCommandHandler { private final MongoClient mongoClient; // 构造函数注入MongoClient public MongoCommandHandler(MongoClient mongoClient) { this.mongoClient = mongoClient; } // 你的私有方法 private Document runCommand(final String command) { final MongoDatabase db = mongoClient.getDatabase("admin"); final Bson bsonDocument = Document.parse(command).toBsonDocument(); final Publisher<Document> commandResult = db.runCommand(bsonDocument); return Mono.from(commandResult).block(); } // 调用私有方法的公共方法(优先测试公共方法) public Document executeAdminCommand(String command) { return runCommand(command); } }
步骤2:编写Mockito测试用例
注意:MongoDatabase是final类,Mockito默认无法mock,需要引入mockito-inline依赖支持final类的mock。
测试公共方法(推荐)
通过调用公共方法间接覆盖私有方法:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoDatabase; import org.bson.Bson; import org.bson.Document; import reactor.core.publisher.Mono; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class MongoCommandHandlerTest { @Mock private MongoClient mongoClient; @Mock private MongoDatabase mongoDatabase; @Test void executeAdminCommand_ShouldReturnExpectedResult() { // 1. 准备预期返回的文档 Document expectedDoc = new Document("ok", 1.0).append("msg", "command executed"); // 2. 配置mock行为 when(mongoClient.getDatabase("admin")).thenReturn(mongoDatabase); // 让runCommand返回预定义的Mono,确保Mono.from().block()能拿到预期数据 when(mongoDatabase.runCommand(any(Bson.class))).thenReturn(Mono.just(expectedDoc)); // 3. 创建被测实例并调用方法 MongoCommandHandler handler = new MongoCommandHandler(mongoClient); Document actualDoc = handler.executeAdminCommand("{ping: 1}"); // 4. 验证结果 assertEquals(expectedDoc, actualDoc); } }
直接测试私有方法(仅特殊场景使用)
如果必须直接测试私有方法,可通过Java反射调用:
import java.lang.reflect.Method; @Test void testPrivateRunCommand() throws Exception { // 准备mock数据和配置 Document expectedDoc = new Document("ok", 1.0); when(mongoClient.getDatabase("admin")).thenReturn(mongoDatabase); when(mongoDatabase.runCommand(any(Bson.class))).thenReturn(Mono.just(expectedDoc)); MongoCommandHandler handler = new MongoCommandHandler(mongoClient); // 通过反射获取并调用私有方法 Method runCommandMethod = MongoCommandHandler.class.getDeclaredMethod("runCommand", String.class); runCommandMethod.setAccessible(true); Document actualDoc = (Document) runCommandMethod.invoke(handler, "{ping: 1}"); assertEquals(expectedDoc, actualDoc); }
关键说明
- 无需单独mock
Mono.from().block(),只需让db.runCommand返回包含预期数据的Mono,Mono.from()会直接复用这个流,block()自然能得到目标结果。 - 单元测试优先覆盖公共方法,私有方法的逻辑应通过公共接口的测试验证,避免依赖私有方法的实现细节。
内容的提问来源于stack exchange,提问作者junelane
相关产品推荐
相关产品推荐

