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

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实例,替代真实连接;
  • 若开启乱序校验,必须MockisRecordOutOfOrder的返回值,否则会执行真实逻辑导致结果不符合预期;
  • 测试时尽量用具体参数替代anyString(),方便后续排查问题。

内容的提问来源于stack exchange,提问作者Daniel Castro Jara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 22:22:49