Mock DocumentSnapshot.exists()始终返回false的问题排查求助
问题排查:Mockito模拟Firestore依赖后recordExists始终返回false
我正在测试一个验证记录存在性的方法,核心需求是确认记录是否存在。用Mockito模拟了所有必要依赖,但设置when(documentSnapshot.exists()).thenReturn(true)后,实际调用recordExists方法始终返回false,怀疑Mock创建方式有误,请求排查原因。
待测试方法
@Override public boolean recordExists(String primaryKey, String payload, Map<String, String> headers) { try { if (this.db == null) { setupConnection(); } if (this.db != null) { if (this.config.getPrimaryKeyValuePrefix() != null && this.config.getPrimaryKeyValuePrefix().length() > 0) { primaryKey = this.config.getPrimaryKeyValuePrefix() + primaryKey; } if (this.config.getPrimaryKeyValueSuffix() != null && this.config.getPrimaryKeyValueSuffix().length() > 0) { primaryKey += this.config.getPrimaryKeyValueSuffix(); } DocumentReference docRef = this.db.collection(this.config.getTableName()).document(primaryKey); ApiFuture<DocumentSnapshot> future = docRef.get(); DocumentSnapshot document = future.get(); if (document.exists()) { if (this.config.getOutOfOrderValidationRequired()) { // check if this message is out of order boolean isOutOfOrder = isRecordOutOfOrder(document, payload, headers); if (isOutOfOrder) { LOG.info( "FirestoreDB recordExists() document is found and message is out of order. PrimaryKey is " + primaryKey + " and payload is " + payload); } return isOutOfOrder; } else { LOG.info("FirestoreDB recordExists() document is found. PrimaryKey is " + primaryKey + " and payload is " + payload); return true; } } else { return false; } } } catch (Exception ex) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); ex.printStackTrace(pw); LOG.error("FirestoreDB setupConnection() exception " + sw.toString()); } return true; }
测试代码
@Test public void recordExistTest() throws JsonProcessingException, ExecutionException, InterruptedException { configMap.put(ConfigUtils.DLQ_DATABASE_CONFIG, "{ \"databaseType\": \"firestore\", \"projectId\": \"data-test\", \"tableName\": \"test\", \"primaryKeyPath\": \"key\", \"primaryKeyValuePrefix\": \"test\", \"outOfOrderValidationRequired\": \"true\"}"); dlqDatabaseConfig = objectMapper.readValue(this.configMap.get(ConfigUtils.DLQ_DATABASE_CONFIG), DatabaseConfig.class); FirestoreDB firestoreDB = new FirestoreDB(dlqDatabaseConfig); DocumentReference documentReference = mock(DocumentReference.class); ApiFuture<DocumentSnapshot> apiFuture = mock(ApiFuture.class); DocumentSnapshot documentSnapshot = mock(DocumentSnapshot.class); when(documentReference.get()).thenReturn(apiFuture); when(apiFuture.get()).thenReturn(documentSnapshot); when(documentSnapshot.exists()).thenReturn(true); when(documentSnapshot.get("myField")).thenReturn("myValue"); assertEquals(true, documentSnapshot.exists()); assertTrue(firestoreDB.recordExists(anyString(), anyString(), anyMap())); }
断言错误栈
java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) at org.junit.Assert.assertTrue(Assert.java:52) at org.rf.kafka.common.databases.FirestoreDBTest.recordExistTest(FirestoreDBTest.java:49) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
问题原因与解决方案
核心问题1:Mock对象未注入被测类
你创建的DocumentReference、ApiFuture、DocumentSnapshot Mock完全独立于FirestoreDB实例的db对象。被测方法里是通过this.db.collection(...).document(...)获取DocumentReference,你的Mock根本没被用到,实际调用的是真实Firestore连接(或未初始化的连接),导致后续逻辑走返回false的分支。
核心问题2:乱序校验分支返回false
配置中outOfOrderValidationRequired设为true,此时方法会返回isRecordOutOfOrder的结果。如果这个方法没被Mock,真实逻辑可能返回false,最终导致断言失败。
修正后的测试代码
@Test public void recordExistTest() throws JsonProcessingException, ExecutionException, InterruptedException { // 1. 初始化配置 configMap.put(ConfigUtils.DLQ_DATABASE_CONFIG, "{ \"databaseType\": \"firestore\", \"projectId\": \"data-test\", \"tableName\": \"test\", \"primaryKeyPath\": \"key\", \"primaryKeyValuePrefix\": \"test\", \"outOfOrderValidationRequired\": \"true\"}"); dlqDatabaseConfig = objectMapper.readValue(this.configMap.get(ConfigUtils.DLQ_DATABASE_CONFIG), DatabaseConfig.class); // 2. 链式Mock所有Firestore依赖 Firestore mockFirestore = mock(Firestore.class); CollectionReference mockCollection = mock(CollectionReference.class); DocumentReference documentReference = mock(DocumentReference.class); ApiFuture<DocumentSnapshot> apiFuture = mock(ApiFuture.class); DocumentSnapshot documentSnapshot = mock(DocumentSnapshot.class); // 3. 定义Mock调用链的行为 when(mockFirestore.collection(dlqDatabaseConfig.getTableName())).thenReturn(mockCollection); when(mockCollection.document(anyString())).thenReturn(documentReference); when(documentReference.get()).thenReturn(apiFuture); when(apiFuture.get()).thenReturn(documentSnapshot); when(documentSnapshot.exists()).thenReturn(true); // 4. Mock乱序校验方法(因为配置开启了该功能) FirestoreDB firestoreDB = spy(new FirestoreDB(dlqDatabaseConfig)); when(firestoreDB.isRecordOutOfOrder(documentSnapshot, anyString(), anyMap())).thenReturn(true); // 5. 将Mock的Firestore注入被测类(需给FirestoreDB添加setDb方法,或用反射注入) firestoreDB.setDb(mockFirestore); // 6. 执行断言(建议用具体参数,避免anyString()) assertTrue(firestoreDB.recordExists("testKey", "testPayload", Collections.emptyMap())); }
补充说明
- 必须把Mock的
Firestore对象注入FirestoreDB实例,替代真实连接; - 若开启乱序校验,必须Mock
isRecordOutOfOrder的返回值,否则会执行真实逻辑导致结果不符合预期; - 测试时尽量用具体参数替代
anyString(),方便后续排查问题。
内容的提问来源于stack exchange,提问作者Daniel Castro Jara
相关产品推荐
相关产品推荐

