【问题标题】:How to load cross-module resources/properties in Maven tests?如何在 Maven 测试中加载跨模块资源/属性?
【发布时间】:2010-06-05 17:15:03
【问题描述】:

我有一个 Maven 维护的项目,其中包含一些模块。一个模块包含一个 XML 文件和一个解析类。

第二个模块依赖于第一个模块。第一个模块中有一个类调用了解析类,但是Maven无法测试第二个模块中的类。测试报告:

java.lang.NullPointerException
 at java.util.Properties.loadFromXML(Properties.java:851)
 at foo.firstModule.Parser.<init>(Parser.java:92)
 at foo.secondModule.Program.<init>(Program.java:84)

Parser.java(第一个模块)中,它使用PropertiesInputStream 来读取/解析XML 文件:

InputStream xmlStream = getClass().getResourceAsStream("Data.xml");
Properties properties = new Properties();
properties.loadFromXML(xmlStream);

Data.xml 位于第一个模块的 resources/foo/firstModule 目录中,并在第一个模块中测试 OK。

似乎在测试第二个模块时,Maven 无法从第一个模块正确加载 Data.xml

我认为我可以通过使用 ma​​ven-dependency-plugin:unpack 来解决问题。所以我将这些 sn-ps 添加到第二个模块的 POM 文件中:

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-dependency-plugin</artifactId>
 <version>2.1</version>
  <executions>
    <execution>
      <id>data-copying</id>
      <phase>test-compile</phase>
      <goals>
        <goal>unpack</goal>
      </goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>foo</groupId>
            <artifactId>firstModule</artifactId>
            <type>jar</type>
            <includes>foo/firstModule/Data.xml</includes>
            <outputDirectory>${project.build.directory}/classes</outputDirectory>
          </artifactItem>
        </artifactItems>
      </configuration>
    </execution>        
  </executions>
</plugin>

在这个 POM 文件中,我解压第一个模块并将 Data.xml 复制到 classes/foo/firstModule/ 目录中,然后运行测试。

可以看到确实是复制到了正确的目录下,但是Maven测试还是读不出来(Properties.loadFromXML() throws NPE)。

我还尝试了不同的输出目录,例如 ${project.build.directory}/resources${project.build.directory}/test-classes ,但一切都是徒劳的。

环境:Maven 2.2.1、eclipse、m2eclipse

----更新----

我忘了提到,第二个模块中的程序,扩展第一个模块中的解析器和解析器的构造函数属性被加载和解析。事实上,Program 是另一个具有更多功能的 Parser。

我认为 Program extends Parser 可能会导致问题(即ClassLoader 问题)。

如果我断开“继承”并在程序中初始化一个新的解析器,它可以正常工作并且测试通过了! 但是,由于它的设计方式,我无法更改继承。

---- 更新完整代码----

这是第一个模块中的Parser

package foo.firstModule;

import java.io.IOException;
import java.io.InputStream;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;

public class Parser
{
  private Properties properties;
  
  public Parser()
  {
    InputStream xmlStream = getClass().getResourceAsStream("Data.xml");
    properties = new Properties();
    try
    {
      properties.loadFromXML(xmlStream);
    }
    catch (InvalidPropertiesFormatException e)
    {
      e.printStackTrace();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
  }
  
  public Properties getProperties()
  {
    return properties;
  }
}

这是Parser's 测试用例,通过了。

package foo.firstModule;

import junit.framework.TestCase;

public class ParserTest extends TestCase
{
  public void testParser()
  {
    Parser p = new Parser();
    assertEquals(64 , p.getProperties().size());
  }
}

这是 secondModule 中的 ParserExtend,它扩展了 firstModule 中的 Parser

package foo.secondModule;

import java.util.Properties;

import foo.firstModule.Parser;

public class ParserExtend extends Parser
{
  private Properties properties;
  
  public ParserExtend()
  {
    this.properties = getProperties();
  }
  
  public int getSize()
  {
    return properties.size();
  }
}

这是ParserExtend's 测试用例:

package foo.secondModule;

import junit.framework.TestCase;

public class ParserExtendTest extends TestCase
{
  public void testParserExtend()
  {
    ParserExtend pe = new ParserExtend();
    assertEquals(64 , pe.getSize());
  }
}

上述测试用例失败,因为 Properties.loadFromXML(Properties.java:851) 抛出 NPE。

但是,如果我不扩展 Parser 而只是初始化一个新的 Parser

package foo.secondModule;

import java.util.Properties;

import foo.firstModule.Parser;

public class ParserInit
{
  private Properties properties;
  
  public ParserInit()
  {
    Parser p = new Parser();
    this.properties = p.getProperties();
  }
  
  public int getSize()
  {
    return properties.size();
  }
}

并使用以下方法对其进行测试:

package foo.secondModule;

import junit.framework.TestCase;

public class ParserInitTest extends TestCase
{
  public void testParserInit()
  {
    ParserInit pi = new ParserInit();
    assertEquals(64 , pi.getSize());
  }
}

测试用例通过!

这是我的整个测试场景。 如何通过ParserExtend's 测试用例?

【问题讨论】:

    标签: unit-testing testing maven-2 maven


    【解决方案1】:

    似乎在测试第二个模块时,maven无法正确加载第一个模块中的Data.xml。

    我创建了完全相同的结构,但无法重现您的问题。我在第二个模块中的 Program 类使用第一个模块中的 Parser 类,它从 XML 文件中加载属性没有任何问题。在 Eclipse 和命令行下测试。

    我想我可以通过maven-dependency-plugin:unpack来解决这个问题。

    使用dependency:unpack 绝对不是解决方案,如果第一个JAR 包含foo/firstModule/Data.xml,则使用另一个模块中的Parser 类应该可以正常工作。其他地方一定有问题。如果你可以上传一个有代表性的测试项目,那么请这样做,我会看看 ti。如果没有允许重现的项目,恐怕你会得到的最佳答案是“调试你的代码”:)

    【讨论】:

    • 嗨,请尝试让 Program extends Parser ,并在 Parser 的构造函数中,做解析工作,然后重新运行测试......(这是我的架构的工作原理......)如果它适合你,我必须更深入地检查我的代码......
    • @smallufo:请更新您的问题以显示您的代码,以便我可以重现。
    • 谢谢,我在问题的尾部添加了更多的 cmets。
    • @smallufo 很抱歉,但我不会尝试猜测您的代码是如何工作的(而且我不会第三次要求您展示一些允许重现的代码)。
    • 感谢您的回复。我已将完整的代码附加到测试用例中……希望对您有所帮助……
    【解决方案2】:

    我同意,这听起来像是类加载器问题。

    如果您总是打算将 Data.xml 放在 /foo/firstModule/Data.xml 位置,请在 foo.firstModule.Parser 中尝试一下:

    InputStream xmlStream = getClass().getResourceAsStream("/foo/firstModule/Data.xml");

    或者,如果您明确希望找到“本地”Data.xml 的行为应该在其他地方重用 firstModule(例如 secondModule 或 thirdModule 有自己的 Data.xml),试试这个:

    InputStream xmlStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("Data.xml");

    【讨论】:

    • 您好,感谢您的回复。如果我更改为 Thread.currentThread().getContextClassLoader().getResourceAsStream("Data.xml"); ParserTest 也会失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 2011-02-28
    相关资源
    最近更新 更多