无法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
相关产品推荐
相关产品推荐

