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

无法Mock @Slf4j提供的Logger,单元测试遇阻求助

Mock @Slf4j Logger失败的解决方案

@Slf4j注解会为类生成一个private static final Logger log的静态字段,普通的Mockito @Mock无法直接替换这个静态字段,导致Mock失效。下面提供几种可行的解决方式:

方案1:反射修改静态Logger字段

通过反射强行修改静态字段的值为Mock对象,无需额外依赖:

服务类(示例)

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class EmailNotificationService {

    public void sendEmail(String recipient, String content) {
        try {
            // 业务逻辑
            log.info("发送邮件给{},内容:{}", recipient, content);
        } catch (Exception e) {
            log.error("发送邮件失败:{}", e.getMessage(), e);
            throw new RuntimeException("邮件发送失败", e);
        }
    }
}

测试类实现

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import java.lang.reflect.Field;

@ExtendWith(MockitoExtension.class)
public class EmailNotificationServiceTest {

    @InjectMocks
    private EmailNotificationService emailService;

    @Mock
    private Logger log;

    @BeforeEach
    void setUp() throws NoSuchFieldException, IllegalAccessException {
        // 获取类的静态log字段
        Field logField = EmailNotificationService.class.getDeclaredField("log");
        // 允许访问私有字段
        logField.setAccessible(true);
        // 替换静态字段值为Mock对象(静态字段第一个参数传null)
        logField.set(null, log);
    }

    @Test
    void testSendEmailFailure() {
        // 执行测试逻辑
        emailService.sendEmail("test@example.com", "测试内容");
        
        // 可选:验证Logger方法调用
        // verify(log).error(anyString(), anyString(), any(Exception.class));
    }
}

方案2:使用PowerMock直接Mock LoggerFactory

如果项目允许引入PowerMock,可以更简洁地Mock静态Logger生成逻辑:

依赖引入(Maven示例)

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

测试类实现

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.mockito.InjectMocks;
import org.mockito.Mock;

@RunWith(PowerMockRunner.class)
@PrepareForTest(LoggerFactory.class) // 标记需要Mock的静态类
public class EmailNotificationServiceTest {

    @InjectMocks
    private EmailNotificationService emailService;

    @Mock
    private Logger log;

    @Test
    void testSendEmailFailure() {
        // Mock LoggerFactory.getLogger方法,返回我们的Mock Logger
        PowerMockito.mockStatic(LoggerFactory.class);
        PowerMockito.when(LoggerFactory.getLogger(EmailNotificationService.class)).thenReturn(log);

        // 执行测试
        emailService.sendEmail("test@example.com", "测试内容");

        // 验证Logger调用
        // verify(log).error(anyString(), anyString(), any(Exception.class));
    }
}

方案3:调整Logger注入方式(非静态)

修改服务类,将Logger改为实例字段,通过构造注入,适合允许调整业务代码的场景:

修改后的服务类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class EmailNotificationService {

    private final Logger log;

    // 构造注入Logger(默认构造用于生产,带参数构造用于测试)
    public EmailNotificationService() {
        this.log = LoggerFactory.getLogger(EmailNotificationService.class);
    }

    public EmailNotificationService(Logger log) {
        this.log = log;
    }

    public void sendEmail(String recipient, String content) {
        try {
            log.info("发送邮件给{},内容:{}", recipient, content);
        } catch (Exception e) {
            log.error("发送邮件失败:{}", e.getMessage(), e);
            throw new RuntimeException("邮件发送失败", e);
        }
    }
}

测试类实现

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;

@ExtendWith(MockitoExtension.class)
public class EmailNotificationServiceTest {

    @Mock
    private Logger log;

    @InjectMocks
    private EmailNotificationService emailService;

    @Test
    void testSendEmailFailure() {
        emailService.sendEmail("test@example.com", "测试内容");
        // 验证Logger调用
        // verify(log).error(anyString(), anyString(), any(Exception.class));
    }
}

内容的提问来源于stack exchange,提问作者Shivang Mangal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:25:12