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

批量运行测试时Mock无交互报错,单测正常的技术求助

问题描述

批量运行测试用例时出现以下错误,但单独运行每个测试用例无异常:

Wanted but not invoked:
logger.error("Error message 2");
-> at acme.logger.MyCoreLogTest.errorWith(MyCoreLogTest.java:74)
Actually, there were zero interactions with this mock.

相关代码

MyCoreLog类

package acme.mycore.logger;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class MyCoreLog {
    private static final Logger logger = LogManager.getLogger("MyCoreLog");
    
    public static final String id = "my_id";
    
    public static void error(String message) {
        if (System.getenv(id) != null)
            logger.error(String.format("%s, %s=%s", message, id, System.getenv(id)));
        else
            logger.error(message);
    }
}

CoreLogTest测试类

package acme.mycore.logger;

import org.apache.logging.log4j.LogManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.apache.logging.log4j.Logger;
import org.mockito.MockedStatic;
import static org.mockito.Mockito.*;


class CoreLogTest {
    MockedStatic<LogManager> logManager;
    @BeforeEach
    public void setUp()
    {
        logManager = mockStatic(LogManager.class);
    }

    @AfterEach
    public void tearDown() throws Exception
    {
        logManager.close();
    }
    
    
    @Test
    void error() {
        String errorMessage = "error message";

        Logger logger = mock(Logger.class);
        logManager.when(() -> LogManager.getLogger("MyCoreLog")).thenReturn(logger);

        MyCoreLog.error(errorMessage);

        verify(logger).error(errorMessage); // 执行正常
    }
    
    @Test
    void errorWith() {
        String errorMessage = "error message 2";

        Logger logger = mock(Logger.class);
        logManager.when(() -> LogManager.getLogger("MyCoreLog")).thenReturn(logger);

        MyCoreLog.error(errorMessage);

        verify(logger).error(errorMessage);  // 报错位置
    }
}
问题原因
  1. 静态变量类加载复用:MyCoreLog中的logger是static final修饰的静态变量,会在类第一次被加载时初始化。批量运行测试时,第一个测试error()执行时MyCoreLog类被加载,此时通过mockStatic(LogManager)生成的mock logger被赋值给静态变量;第二个测试errorWith()执行时,MyCoreLog类已加载完成,静态logger不会重新初始化,导致第二个测试中设置的新mock logger从未被使用,verify校验的是未被调用的新mock,因此报错。
  2. 测试逻辑错误:errorWith()测试的校验逻辑本身有误——当环境变量my_id存在时,MyCoreLog.error()会调用logger.error()输出格式化后的消息,而非原始的errorMessage,这也是问题暴露的潜在因素。
解决方案

推荐重构MyCoreLog类,避免静态依赖的强耦合,同时修正测试逻辑:

步骤1:修改MyCoreLog类

添加静态方法用于测试时替换logger:

package acme.mycore.logger;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class MyCoreLog {
    private static Logger logger = LogManager.getLogger("MyCoreLog");
    
    public static final String id = "my_id";
    
    // 测试专用:替换logger实例
    static void setLogger(Logger customLogger) {
        logger = customLogger;
    }
    
    public static void error(String message) {
        if (System.getenv(id) != null)
            logger.error(String.format("%s, %s=%s", message, id, System.getenv(id)));
        else
            logger.error(message);
    }
}

步骤2:修改测试类

移除静态LogManager的mock,直接替换MyCoreLog的logger,并修正测试逻辑:

package acme.mycore.logger;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;

class CoreLogTest {
    private Logger mockLogger;

    @BeforeEach
    public void setUp() {
        mockLogger = mock(Logger.class);
        MyCoreLog.setLogger(mockLogger);
        // 清除环境变量,避免测试间干扰
        System.clearProperty(MyCoreLog.id);
    }

    @AfterEach
    public void tearDown() {
        // 恢复原始logger,避免影响其他测试
        MyCoreLog.setLogger(LogManager.getLogger("MyCoreLog"));
    }
    
    @Test
    void error() {
        String errorMessage = "error message";

        MyCoreLog.error(errorMessage);

        verify(mockLogger).error(errorMessage);
    }
    
    @Test
    void errorWith() {
        String errorMessage = "error message 2";
        // 设置环境变量,测试格式化分支
        System.setProperty(MyCoreLog.id, "test_value");

        MyCoreLog.error(errorMessage);

        String expectedMessage = String.format("%s, %s=%s", errorMessage, MyCoreLog.id, "test_value");
        verify(mockLogger).error(expectedMessage);
    }
}

内容的提问来源于stack exchange,提问作者Gabriel Muñumel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:17:49