【问题标题】:Retrieving path to handle files making troubles during testing检索路径以处理在测试期间出现问题的文件
【发布时间】:2013-09-09 00:20:48
【问题描述】:

我正在为我的程序开发我的 Ant 构建器,特别是处理文件的路径。

我找到了一个非常简单的解决方案,在我的常量界面中添加了两个字段:

/** The actual jar file where the class is run */
public static final File JAR_FILE = new File(MagicHogwarts.class.getProtectionDomain().getCodeSource().getLocation().getPath());

/** The path to the main folder where the file .jar is run */
public static final String BASE_DIRECTORY = JAR_FILE.getAbsolutePath().replace(JAR_FILE.getName(), "");

在每个处理文件的方法中,我只是询问 jar 所在的主文件夹的相对路径,然后首先添加 BASE_DIRECTORY,这至少在 macOSX 中都可以正常工作,无论是在运行 jar 的 eclipse 中。

对这个快速解决方案感到满意,我运行了我的测试。那失败了。 问题似乎出在Constants接口,无法创建JAR_FILE,因为getLocation返回null,然后getPath失败。

但我不明白为什么。

所以我尝试模拟接口,由于 var 被声明为 static final,所以我尝试使用 PowerMock:

@RunWith(PowerMockRunner.class)
@PrepareForTest({GameWindow.class,Constants.class})
public class MapReaderTest {

@Test
public void testReadingExampleMap() throws Exception {
    mockStatic(GameWindow.class);
            mockStatic(Constants.class);
    TextureLoader tl = createMock(TextureLoader.class);
    Texture tx = createMock(Texture.class);
    expect(GameWindow.getTextureLoader()).andReturn(tl);
            expect(Constants.BASE_DIRECTORY).andReturn("/Users/Gianmarco/Documents/java/MagicHogwarts/").anyTimes();
    expect(tl.getTexture("/Users/Gianmarco/Documents/java/MagicHogwarts/bin/mh/test/sewer_tileset.png")).andReturn(tx);
    expect(tx.getImageHeight()).andReturn(32).anyTimes();
    expect(tx.getImageWidth()).andReturn(32).anyTimes();
    replay(GameWindow.class);
            replay(Constants.class);
    replay(tl);
    replay(tx);

    // Act
    Map map = new TMXMapReader().readMap("/bin/mh/test/sewers.tmx");//mapFile.getAbsolutePath());

    // Assert
    assertEquals(Map.ORIENTATION_ORTHOGONAL, map.getOrientation());
    assertEquals(50, map.getHeight());
    assertEquals(50, map.getHeight());
    assertEquals(24, map.getTileWidth());
    assertEquals(24, map.getTileHeight());
    assertEquals(2, map.getLayerCount());
}
}

但是我得到了一个ExceptionInInitializerError,第60行Constants,也就是声明JAR_FILE的那一行,错误是由NullPointerException引起的。

我想的下一步是将接口转换为类,也许 powermock 无法以这种方式处理接口。

运行,得到一个 NoClassDefFoundError。 仍然没有结果,我不知道该怎么办。所以我尝试了反射。

我使用了这个静态方法:

static void setFinalStatic(Field field, Object newValue) throws Exception {
    field.setAccessible(true);

    // remove final modifier from field
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

    field.set(null, newValue);
}

调用它:

setFinalStatic(Constants.class.getField("JAR_FILE"), null);
setFinalStatic(Constants.class.getField("BASE_DIRECTORY"), "my/path/hardcoded");

但这都不起作用,我得到了一个 ExceptionInInitializerError 堆栈,其中包含 sun.mic.Unsafe.ensureClassInitialized(Native Method)、sun.reflect 和其他。

我花了几个小时来解决这个问题,我得出的结论是问题出在 JAR_FILE 以及用于初始化它的方法上,因为即使从常量中删除 final 修饰符我也无法更改值打电话给Constants.JAR_FILE = null

我为解决问题所做的事情是:

/** The actual jar file where the class is run */
    public static final File    JAR_FILE            = (MagicHogwarts.class.getProtectionDomain().getCodeSource().getLocation() == null ? null : new File(MagicHogwarts.class.getProtectionDomain().getCodeSource().getLocation().getPath()));

    /** The path to the main folder where the file .jar is run */
    public static final String  BASE_DIRECTORY      = (JAR_FILE != null ? JAR_FILE.getAbsolutePath().replace(JAR_FILE.getName(), "") : "/Users/Gianmarco/Documents/java/MagicHogwarts/");

这是使用 if 语句的粗鲁、非常不愉快和可怕的方式。

我的问题是,是否有任何不同的方法可以从终端行获取 jar 的基本目录和/或使用 #java Myclass 调用的文件,这两种方法都可以在正常处理中使用(也可以是 jar ) 和测试

我达到的解决方案是有效的,但我认为将一些测试代码硬编码到一个类中并不是一件好事,实际上使用 if 就像告诉常量类“如果我正在测试,给我这个模拟字符串,否则给我真正的道路”。如果在正常程序行为中没有用,我希望将我的测试代码与我的类分开(应该如此)。

【问题讨论】:

  • 您可以使用多种技巧来获取 JAR 文件的位置,并且您已经尝试了一些。听起来好像在运行测试时,您正在针对原始 .class 文件运行,并且 is 没有 JAR 文件。寻找一种方法来利用 Class.getResource()Class.getResourceAsStream() 并在测试类路径中显示您需要的内容,而不是在磁盘上查找文件。另外,请记住静态常量字符串是内联的,用于可能发生变化的事物是危险的。

标签: java eclipse testing ant powermock


【解决方案1】:

在测试时不应依赖绝对路径,因为这会使测试变得脆弱且不可移植。

应该寻找资源,而不是绝对路径。最好使用 Class.getResource 或 Class.getResourceAsStream 读取。

对于前者,您可以检索 URL。如有必要,可以使用 url.toExternalForm 将其转回绝对路径,但是一旦您拥有所需的资源,这应该就足够了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多