场景描述
spring 项目中配置了 logback 收集日志,同时使用 @SpringBootTest 做单元测试。此时就报错了,文件找不见。
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[file] - Failed to create parent directories for [/home/zhu/app/logs/2022-09-07.log]
ERROR in ch.qos.logback.core.rolling.RollingFileAppender[file] - openFile(null,true) call failed. java.io.FileNotFoundException: /home/zhu/app/logs/2022-09-07.log (No such file or directory)
原因
logback-spring.xml
文件中这么写的,bosc.rec.logSavePath 指定保存日志文件,可以在 application.yml
指定。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" packagingData="true">
<springProperty scope="context" name="LOG_HOME" source="bosc.rec.logSavePath" />
<!-- 中间内容省略了 -->
</configuration>
# application.yml
bosc:
rec:
logSavePath: ${
LOG_SAVE_PATH:/home/zhu/app/logs}
当单元测试启动时先加载 logback 的配置,再执行测试 case。日志文件找不到,就会抛出上面的异常。而且使用 @Before
, @BeforeAll
或者 static
方法,在内部 System.setProperty()
设置环境变量的方式也都不能解决问题。因为这些方法的执行都晚于 logback 配置的加载。
解决办法
直接上代码。
public class PropertyExtension implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) {
System.setProperty("LOG_SAVE_PATH", "/Users/mac/Downloads/bosc/logs");
}
}
@ExtendWith(PropertyExtension.class)
@SpringBootTest(classes = JobManageApplication.class)
class ScenePipelineRepositoryImplTest {
@Autowired
ScenePipelineRepository repository;
@Test
public void save_ScenePipeline_succeed() {
// 内容省略
}
啰嗦几句
之所以抛出异常是因为使用 @SpringBootTest 做单元测试,这个注解会在执行测试 case 时会启动整个 spring 服务,所以会执行 spring 的初始化过程,加载一大堆的东西,从而包括了 logback 这玩意。
但这样使用是不合理的:
原因一:单测只测一个功能点,有必要把整个服务都启动吗?又不是做集成测试。
原因二:假如服务中有定时任务 或者 向其他服务发请求的功能,那么这个任务也会运行,这是单测不想看到的现象。
@SpringBootTest 适合做集成测试,测试成功后采用 @ignore 将测试类注解掉。
spring 三层结构,每层都有单测,每层测试的目的和方式都不一样。
要让团队成员都遵守一些规矩还是有些挑战的。