You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 9模块中Gradle+Jigsaw项目JUnit测试资源文件放置位置问询

解决Java 9模块(Jigsaw)+ Gradle环境下JUnit测试资源访问问题

我之前也碰到过这个坑,Java 9的模块系统把资源和类一样做了封装,不再是全局可访问的,再加上Gradle的Jigsaw插件对模块打包的特殊处理,确实容易踩资源找不到的问题。下面给你一步步说清楚怎么解决:

核心原因

Java 9模块系统下,资源属于特定模块的一部分,只有当模块明确开放资源所在的路径(或者资源在模块内部被访问)时,代码才能加载到它。Gradle的java-module-info插件会严格遵循模块规则来打包主代码和测试代码的资源,所以光放对位置还不够,得配合module-info配置才行。

资源存放位置(依然可以用熟悉的路径)

你完全可以继续把测试资源放在src/test/resources/目录下——不管是放在根目录(比如src/test/resources/foo.txt)还是某个包路径下(比如src/test/resources/com/example/hello/foo.txt),Gradle都会自动把这些资源打包到测试模块的classpath里。

真正关键的是模块描述文件的配置,分两种场景来说:

场景1:测试代码和主代码在同一个模块

比如你的主模块是com.example.hello,测试代码也在这个模块下:

  1. 在src/test/java/下创建测试专用的模块描述文件module-info.test.java(Gradle会自动识别这个文件作为测试模块的配置):
module com.example.hello {
    // 声明依赖JUnit API
    requires org.junit.jupiter.api;
    // 开放测试类所在的包给JUnit,允许它反射调用测试方法
    opens com.example.hello.test to org.junit.jupiter.api;
    // 不需要额外声明资源——模块内部的代码默认可以访问模块内的所有资源
}
  1. 测试代码中访问资源的正确姿势:
    推荐用Java 9新增的Module API来访问,比传统的ClassLoader更贴合模块系统:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.InputStream;

public class ResourceTest {
    @Test
    void testLoadResource() throws Exception {
        // 访问根目录下的foo.txt
        try (InputStream is = getClass().getModule().getResourceAsStream("foo.txt")) {
            assertNotNull(is, "资源文件foo.txt未找到");
            // 这里可以添加资源读取逻辑
        }

        // 如果是放在com/example/hello/下的bar.txt,路径要写完整
        try (InputStream is = getClass().getModule().getResourceAsStream("com/example/hello/bar.txt")) {
            assertNotNull(is, "资源文件bar.txt未找到");
        }
    }
}

如果非要用ClassLoader,记得去掉路径前的斜杠(模块系统中,ClassLoader.getResource("foo.txt")是相对于模块根目录查找,带斜杠的绝对路径在模块环境下会失效):

URL resource = ClassLoader.getSystemClassLoader().getResource("foo.txt");
assertNotNull(resource);

场景2:测试代码在独立的测试模块

如果你的测试代码是一个单独的模块(比如主模块com.example.hello,测试模块com.example.hello.test):

  1. 主模块的src/main/java/module-info.java需要开放资源所在的路径给测试模块:
    比如主资源放在src/main/resources/foo.txt(根目录),就要开放根目录给测试模块:
module com.example.hello {
    exports com.example.hello;
    // 开放根目录的资源给测试模块,允许它访问
    opens . to com.example.hello.test;
}

如果资源放在某个包下(比如src/main/resources/com/example/hello/foo.txt),就开放对应的包:

module com.example.hello {
    exports com.example.hello;
    opens com.example.hello to com.example.hello.test;
}
  1. 测试模块的src/test/java/module-info.test.java要声明依赖主模块和JUnit:
module com.example.hello.test {
    requires com.example.hello;
    requires org.junit.jupiter.api;
    opens com.example.hello.test to org.junit.jupiter.api;
}
  1. 测试代码中访问主模块的资源,路径规则和场景1一致;测试模块自己的资源(src/test/resources/下的文件)直接访问即可。

Gradle配置注意事项

确保你的build.gradle正确引入了java-module-info插件,并且配置了JUnit平台:

plugins {
    id 'java'
    id 'java-module-info' // 关键:启用Jigsaw模块支持
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.+'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.+'
}

test {
    useJUnitPlatform() // 启用JUnit 5平台
}

常见坑点

  • 不要用带前导斜杠的资源路径(比如/foo.txt):在模块系统中,这种绝对路径会被视为全局路径,但模块资源是隔离的,所以找不到。
  • 别忘了给JUnit开放测试包:如果没加opens your.test.package to org.junit.jupiter.api;,JUnit可能无法反射调用测试方法,甚至间接导致资源加载异常。
  • 测试模块的module-info文件要放在src/test/java/下,命名为module-info.test.java,Gradle才会把它当作测试模块的配置。

内容的提问来源于stack exchange,提问作者michas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:17:18