Android单元测试:读取生产资源时getResourceAsStream返回null问题
getResourceAsStream返回null的问题 我太懂这种头疼的感觉了——生产和Debug模拟器里好好的,一跑单元测试就掉链子,资源加载返回null。咱们先把根因说清楚,再给你两个靠谱的解决方案:
问题根源
本地单元测试是直接在JVM上运行的,不会打包Android项目里的res/raw资源,你用SomeClass.class.getResourceAsStream("/res/raw/some.def")这种类加载器的方式,在纯JVM环境里根本找不到对应的文件路径,自然返回null。而Debug模拟器是运行在Android runtime里,资源会被正常打包和索引,所以能成功加载。
解决方案
方案1:用Robolectric模拟Android环境
如果你的单元测试需要依赖Android的资源系统,Robolectric是最直接的选择:
- 先在项目的
build.gradle(Module级)里添加Robolectric依赖:
testImplementation 'org.robolectric:robolectric:4.12.1' // 替换为最新版本即可
- 在你的测试类上添加注解,指定用Robolectric运行:
@RunWith(RobolectricTestRunner.class) public class SomeManagerTest { // 测试代码 }
- 在测试里通过Android的
Resources类加载raw资源:
InputStream stream = RuntimeEnvironment.application.getResources().openRawResource(R.raw.some);
这种方式完全贴合Android生产环境的资源加载逻辑,测试结果也更准确。
方案2:将测试资源移到JVM类路径下
如果不想引入Robolectric,你可以把测试需要的some.def复制一份到src/test/resources目录下(没有的话自己创建),然后在单元测试里用类加载器读取:
InputStream stream = getClass().getResourceAsStream("/some.def");
因为JVM单元测试的类加载器会自动扫描src/test/resources目录,这样就能直接找到文件了。
额外提醒
其实生产环境里用getResourceAsStream访问res/raw资源是有风险的——Android打包后资源的实际路径会和你写的/res/raw/some.def不一致,只是Debug环境刚好兼容了这种写法。建议生产代码里也改成用Resources.openRawResource(R.raw.some)来加载,更符合Android的规范。
内容的提问来源于stack exchange,提问作者Samuel

