【问题标题】:getResourceAsStream from JUnit来自 JUnit 的 getResourceAsStream
【发布时间】:2013-07-17 18:57:13
【问题描述】:

我正在为需要在初始化期间加载配置文件的项目创建 JUnit 测试用例。

此配置文件位于项目中的 src/main/resources/config 文件夹中,并且在构建 maven 期间将其放入 JAR 内的 /config 文件夹中。

初始化类,使用以下语句从那里读取文件:

ClassLoader classloader = this.getClass().getClassLoader();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream("/config/config.xml")));

我遇到的问题是,当我将这个 jar 部署并执行到应用程序服务器中时,它会按预期工作,但是,每当我在 Eclipse 的 JUnit TestCase 中运行它时,getResrouceAsStream 方法都会返回 null。

考虑到该类是 my.package.MyClassTest.java,并且它位于 src/test/java/my/package/MyClassTest.java 中,我已经尝试将 config.xml 文件的副本放入以下没有成功的文件夹:

- src/test/resources/config
- src/test/resources/my/package/config
- src/test/java/my/package/config

我知道在 StackOverflow 中已经多次询问过类似的问题,但我发现的所有回复都指的是更改文件加载方式,虽然更改代码可能是一种选择,但我更愿意只找到文件的正确位置,所以我不需要修改已经在生产环境中工作的东西。

那么,我应该把这个文件放在哪里才能在我的 JUnit 测试中使用它?

更新

我刚刚想出了解决方案,对代码进行了一些小改动: 我没有使用ClassLoader获取资源,而是直接使用了类:

Class clazz = this.getClass();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(clazz.getResourceAsStream("/config/config.xml")));

并成功从 src/test/resources/config/config.xml 读取文件。

但是,这里有一些非常奇怪的东西: Class.getResourceAsStream 方法是:

public InputStream getResourceAsStream(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResourceAsStream(name);
    }
    return cl.getResourceAsStream(name);
}

如果我调试它,我可以清楚地看到这个 getClassLoader0() 返回与上一个调用完全相同的对象(相同的 id),this.getClass().getResourceAsStream( )(我坚持,只是为了比较值)!!!

这是怎么回事?!

为什么直接调用方法不起作用,而在两者之间插入一个新的方法调用?

老实说,我真的很惊讶。

顺便说一句,我使用的是 JUnit 4.10 版。是否会以某种方式篡改 getClassLoader 调用?

非常感谢,

卡尔斯

【问题讨论】:

  • src/test/resources/ 是 Eclipse 中的源文件夹吗?
  • @SotiriosDelimanolis 如果您将此作为答案发布,我会投票赞成。
  • @SotiriosDelimanolis:是的。确实,当我查看它时,我意识到 Eclipse 默认将它用于 maven 项目。

标签: java junit classloader getresource


【解决方案1】:

回答你的问题

如果我调试它,我可以清楚地看到这个 getClassLoader0() 返回与上一个调用完全相同的对象(相同的 id),this.getClass().getResourceAsStream( )(我坚持,只是为了比较值)!!!

这是怎么回事?!

为什么直接调用方法不起作用,而在两者之间插入一个新的方法调用?

调用的区别

this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");

打电话

this.getClass().getResourceAsStream("/config/config.xml");

位于您从Class 显示的确切来源中:

public InputStream getResourceAsStream(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResourceAsStream(name);
    }
    return cl.getResourceAsStream(name);
}

但问题不在于getClassLoader0() 返回的内容。它在两种情况下都返回相同的东西。区别实际上在resolveName(name)。这是Class 类中的私有方法。

private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    if (!name.startsWith("/")) {
        Class<?> c = this;
        while (c.isArray()) {
            c = c.getComponentType();
        }
        String baseName = c.getName();
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
            name = baseName.substring(0, index).replace('.', '/')
                +"/"+name;
        }
    } else {
        name = name.substring(1);
    }
    return name;
}

所以你看,在实际调用类加载器的getResourceAsStream() 之前,它实际上从路径中删除了起始斜杠。

一般来说,当它没有斜线时,它会尝试获取相对于this的资源,如果它的开头有斜线,则将其传递给classLoader。

classLoader 的getResourceAsStream() 方法实际上旨在用于相对路径(否则您将只使用FileInputStream)。

因此,当您使用this.getClass().getClassLoader().getResourceAsStream("/config/config.xml"); 时,实际上是在开头带有斜杠的路径传递给它,但失败了。当您使用this.getClass().getResourceAsStream("/config/config.xml"); 时,您可以将其删除。

【讨论】:

  • 恐怕我目前无法测试它(我想尝试手动从路由中删除第一个斜线,看看它是否以相同的方式工作),但听起来很有说服力。谢谢,接受了。
【解决方案2】:

ClassLoader 对象中的getResourceAsStream 函数不会删除搜索字符串前面的斜杠,因为搜索将相对于类路径进行。即搜索用于加载类的搜索路径的资源。

例如,如果您的类 yourpackage/Test.class(位于 /a/b/c/d/yourpackage/Test.class 下)由系统类加载器(即默认类加载器)加载,并且您的类路径必须指向 /a/b/c/d 才能加载该类。将在此路径上进行搜索。

getResourceAsStream 来自 Class Object 的函数确实删除了搜索字符串前面的斜杠,因为搜索将相对于它所在的类进行。即搜索加载类的资源。

例如,如果从/a/b/c/d/yourpackage/Test.class 加载yourpackage/Test.class,那么资源路径将为/a/b/c/d/yourpackage/config/config.xml

您可以使用以下代码片段对此进行测试,因为 getResource 和 getResourceAsStream 使用相同的搜索算法。

System.out.println(Test.class.getClassLoader().getResource("config/config.xml"));
System.out.println(Test.class.getResource("config/config.xml"));

【讨论】:

    【解决方案3】:

    我不确定,但您可以尝试将资源文件夹放在 src/main 树中,而不是 src/test 树中。至少在某些配置中,eclipse 将“资源”文件从 src 复制到类,而不是从测试复制到类。值得一试...

    【讨论】:

    • 两个资源文件夹都存在于src/main 和src/test 中,包含相同的文件……但它不起作用。
    猜你喜欢
    • 2011-07-11
    • 1970-01-01
    • 1970-01-01
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    相关资源
    最近更新 更多