我正在为我的程序开发Ant构建器,特别是处理文件路径。
我达到了一个非常简单的解决方案,在我的Constant接口中添加了两个字段:
/** 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,这在运行jar的eclipse中至少在macOSX中工作正常。
对这个快速解决方案感到高兴我运行我的测试。那失败了。
问题似乎是在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,Constants的第60行,即声明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");
但是这两个都没有用,我得到了一个包含sun.mic.Unsafe.ensureClassInitialized(Native Method)
,sun.reflect和其他的堆栈的ExceptionInInitializerError。
我花了好几个小时解决这个问题,我得出的结论是问题出在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的base-dir和/或使用#java Myclass
从终端行调用的文件,可以在正常处理中使用(也jar)和测试?
我达成的解决方案正在运行,但我认为将一些测试代码硬编码到类中并不是一件好事,实际上使用if就像告诉Constants类“如果我正在测试,请给我这个模拟字符串,别的给我真正的道路“。如果在正常的程序行为中没有用,我想把我的测试代码与我的类分开(应该是这样)。
答案 0 :(得分:0)
当测试时,不应该依赖绝对路径,因为它会使测试变得脆弱和不可移植。
应该寻找资源,而不是绝对路径。最好使用Class.getResource或Class.getResourceAsStream来读取。
对于前者,您可以检索URL。如果有必要,可以通过使用url.toExternalForm将其转回到绝对路径,但是一旦拥有了所需的资源就足够了。