Android单元测试使用Netty Unpooled时SLF日志初始化失败怎么解决?
问题根因
- 空指针是类加载阶段触发的:Android版logback的静态初始化逻辑会调用Android系统API获取外部存储路径,本地JVM单元测试环境没有Android运行时,所以直接抛出空指针。
- Mockito模拟不生效的原因是:
StaticLoggerBinder、InternalLoggerFactory的初始化逻辑是在类加载阶段执行的,远早于Mockito的Mock注入、测试方法执行的时机,你写的Mock代码根本没机会生效。
可行解决方案(按优先级排序)
- 方案1:单元测试环境替换日志实现(最推荐)
在模块的build.gradle配置中,给测试环境引入纯JVM的slf4j实现,直接避开Android logback的加载:
dependencies { // 单元测试使用slf4j-simple,不会调用Android相关API testImplementation 'org.slf4j:slf4j-simple:2.0.7' }
依赖引入后,单元测试运行时slf4j会优先绑定slf4j-simple,从根源避免触发Android相关的日志初始化逻辑。
- 方案2:强制指定Netty的日志工厂
在测试类的静态初始化块中,提前指定Netty使用JDK自带的日志实现,绕开slf4j的加载流程:
@RunWith(MockitoJUnitRunner::class) class Jt808AnnotationTest { companion object { init { // 必须在任何Netty类加载前执行,优先设置日志工厂 InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE) } } @Test fun testWrite() { val byteBuf = Unpooled.buffer(256) // 后续测试逻辑 } }
- 方案3:测试环境新增logback配置
在src/test/resources目录下新建logback.xml,使用纯控制台输出的配置,不要引入Android相关的Appender或配置项:
<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="CONSOLE" /> </root> </configuration>
该配置会覆盖默认的Android logback配置,初始化时不会调用AndroidContextUtil。
补充说明(非法反射警告)
你看到的非法反射警告是因为你使用的Netty 4.0.33版本过老,对JDK 9+的兼容性不足,两种解决方式:
- 把单元测试的运行JDK版本降到JDK 8
- 升级Netty到4.1.x最新稳定版
内容的提问来源于stack exchange,提问作者David Guo
相关产品推荐
相关产品推荐

