SpringBoot测试中如何抑制特定用例的异常日志输出
如何在特定Spring Boot测试用例中抑制异常日志输出
我有个加了@SpringBootTest注解、用JUnit Jupiter写的Spring Boot测试用例,目的是验证某个业务方法在特定场景下会抛出ServiceException。这个业务方法会捕获IOException、HttpException等多种异常,打印堆栈日志后重新抛出ServiceException(不是直接抛原异常的反模式)。方法不需要给客户端暴露异常细节,只提示用户稍后重试就行。测试能正常跑过,但每次运行都会输出一堆异常堆栈日志,跟生产环境的错误日志一样,我就想只在这个测试用例里关掉这些日志,别影响其他测试的日志输出。
被测方法示例:
public boolean isThisParameterGoodToUse(Object parameter) throws ServiceException { try { boolean allWasOk = true; // 执行可能抛出各种异常的业务逻辑 return allWasOk; } catch (IOException | HttpException | SomeException | YetAnotherException e) { String msg = "出现问题,请稍后重试。"; this.log.error(msg, e); // 供监控系统用,不需要展示给用户 throw new ServiceException(msg); } }
测试用例示例:
@Test public void isThisParameterGoodToUse() { assertThrows(ServiceException.class, () -> this.injectedService.isThisParameterGoodToUse("这参数完全不合格!")); }
运行测试时会输出类似这样的日志:
com.myProd.services.SomeException: 出现问题,请稍后重试。 at ... <一堆堆栈信息> ...
给你几个实用的解决办法:
办法一:用Mockito Mock日志对象(适配SLF4J+Logback等主流日志框架)
直接把业务类里的日志对象Mock掉,让它别输出日志。如果用了Lombok的@Slf4j,Mockito能自动匹配注入对应的Logger。
代码示例:
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 static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.doNothing; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.any; @ExtendWith(MockitoExtension.class) @SpringBootTest public class YourServiceTest { @Mock private Logger log; // 对应被测类中声明的Logger实例 @InjectMocks private YourService injectedService; @Test public void isThisParameterGoodToUse() { // 让error级别的日志调用不做任何操作 doNothing().when(log).error(anyString(), any(Throwable.class)); assertThrows(ServiceException.class, () -> injectedService.isThisParameterGoodToUse("这参数完全不合格!")); } }
这个办法精准控制单个测试用例的日志,完全不会影响其他测试的日志输出。
办法二:临时调整目标类的日志级别(以Logback为例)
在测试前把业务类的日志级别临时设为OFF,测试结束后再恢复原来的级别,简单直接,不用Mock对象。
代码示例:
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.assertThrows; @SpringBootTest public class YourServiceTest { private Logger targetLogger; private Level originalLevel; @BeforeEach void setUp() { // 获取被测业务类的日志实例 targetLogger = (Logger) LoggerFactory.getLogger(YourService.class); // 保存原来的日志级别 originalLevel = targetLogger.getLevel(); // 临时关闭该类的所有日志输出 targetLogger.setLevel(Level.OFF); } @Test public void isThisParameterGoodToUse() { assertThrows(ServiceException.class, () -> injectedService.isThisParameterGoodToUse("这参数完全不合格!")); } @AfterEach void tearDown() { // 恢复原来的日志级别,避免影响其他测试 targetLogger.setLevel(originalLevel); } }
这个办法不需要修改业务类的依赖,适合不想引入Mockito的场景,同样只针对当前测试用例生效。
内容的提问来源于stack exchange,提问作者Ville Myrskyneva
相关产品推荐
相关产品推荐

