如何在 Java 项目中包含一个资源文件以仅与 new File() 一起使用?
How do I include a resource file in Java project to be used with just new File()?
我正在使用 Java 为 Pig 编写 UDF。它工作正常,但 Pig 没有给我分离环境的选项。我的 Pig 脚本正在做的是从 IP 地址获取地理位置。
这是我在地理位置部分的代码。
private static final String GEO_DB = "GeoLite2-City.mmdb";
private static final String GEO_FILE = "/geo/" + GEO_DB;
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
DatabaseReader reader = new DatabaseReader.Builder(new File(GEO_DB)).build();
// other stuff
}
GeoLite2-City.mmdb
存在于 HDFS 中,这就是为什么我可以使用 /geo/GeoLite2-City.mmdb
从绝对路径引用的原因。
但是,我无法通过我的 JUnit 测试做到这一点,或者我必须在我的本地计算机和 Jenkins 上创建 /geo/GeoLite2-City.mmdb
,这并不理想。我正在尝试找出一种方法让我的测试在使用 new File(GEO_DB)
而不是
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')
因为
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')
在 Hadoop 中不起作用。
如果我 运行 Junit 测试它会失败,因为我的本地机器上没有 /geo/GeoLite2-City.mmdb
。
有没有办法克服这个问题?我只是希望我的测试通过而不更改使用 getClass().getResourceAsStream
的代码,我不能 if/else 解决这个问题,因为 Pig 没有给我一种传递参数的方法,或者我可能丢失了东西。
这是我的 JUnit 测试
@Test
@Ignore
public void shouldGetGeoData() throws Exception {
String ipTest = "128.101.101.101";
Map<String, Object> geoJson = new LogLine2Json().geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
如果我从资源文件夹中读取数据库文件,它会起作用。这就是为什么我有@Ignore
你不知道。您的问题体现了术语上的矛盾。资源不是文件,也不存在于文件系统中。您可以 将文件与 JAR 分开分发并将其用作 File
或 将其包含在 JAR 中并将其用作一种资源。不是都。你要拿定主意。
此外,您的整个代码看起来都无法测试。
每次在生产代码中直接调用 new 时,都会防止依赖注入;从而使测试代码变得更加困难。
重点是不要在生产代码中调用 new File()
。
相反,您可以使用为您提供 "ready to use" DatabaseReader 对象的工厂。然后你可以测试你的工厂做正确的事;并且您可以在测试此代码时模拟该工厂(return 模拟数据库 reader)。
所以,那个文件实例只是您 "testing problems" 此处的顶部。
老实说:不要先编写生产代码。做TDD:先写测试用例;您很快就会了解到,您在此处展示的此类生产代码确实很难测试。当您应用 TDD 时,您从 "test perspective" 开始,您将创建真正可测试的生产代码。
您必须使文件位置可配置。例如。通过构造函数注入它。例如。您可以创建一个仅用于测试的非默认构造函数。
public class LogLine2Json {
private static final String DEFAULT_GEO_DB = "GeoLite2-City.mmdb";
private static final String DEFAULT_GEO_FILE = "/geo/" + GEO_DB;
private final String geoFile;
public LogLine2Json() {
this(DEFAULT_GEO_FILE);
}
LogLine2Json(String geoFile) {
this.geoFile = geoFile;
}
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
File file = new File(geoFile);
DatabaseReader reader = new DatabaseReader.Builder(file).build();
// other stuff
}
}
现在您可以从该资源创建一个文件并在您的测试中使用该文件。
public class LogLine2JsonTest {
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Test
public void shouldGetGeoData() throws Exception {
File dbFile = copyResourceToFile("/geo/GeoLite2-City.mmdb");
String ipTest = "128.101.101.101";
LogLine2Json logLine2Json = new LogLine2Json(dbFile.getAbsolutePath())
Map<String, Object> geoJson = logLine2Json.geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
private File copyResourceToFile(String name) throws IOException {
InputStream resource = getClass().getResourceAsStream(name);
File file = folder.newFile();
Files.copy(resource, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
return file;
}
}
TemporaryFolder 是一个 JUnit 规则,它删除之后测试期间创建的每个文件。
您可以使用 hasToString
匹配器修改断言。如果测试失败,这将为您提供更详细的信息。 (而且你必须 read/write 更少的代码。)
assertThat(geoJson.get("lLa"), hasToString("44.9759"));
assertThat(geoJson.get("lLo"), hasToString("-93.2166"));
我正在使用 Java 为 Pig 编写 UDF。它工作正常,但 Pig 没有给我分离环境的选项。我的 Pig 脚本正在做的是从 IP 地址获取地理位置。
这是我在地理位置部分的代码。
private static final String GEO_DB = "GeoLite2-City.mmdb";
private static final String GEO_FILE = "/geo/" + GEO_DB;
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
DatabaseReader reader = new DatabaseReader.Builder(new File(GEO_DB)).build();
// other stuff
}
GeoLite2-City.mmdb
存在于 HDFS 中,这就是为什么我可以使用 /geo/GeoLite2-City.mmdb
从绝对路径引用的原因。
但是,我无法通过我的 JUnit 测试做到这一点,或者我必须在我的本地计算机和 Jenkins 上创建 /geo/GeoLite2-City.mmdb
,这并不理想。我正在尝试找出一种方法让我的测试在使用 new File(GEO_DB)
而不是
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')
因为
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')
在 Hadoop 中不起作用。
如果我 运行 Junit 测试它会失败,因为我的本地机器上没有 /geo/GeoLite2-City.mmdb
。
有没有办法克服这个问题?我只是希望我的测试通过而不更改使用 getClass().getResourceAsStream
的代码,我不能 if/else 解决这个问题,因为 Pig 没有给我一种传递参数的方法,或者我可能丢失了东西。
这是我的 JUnit 测试
@Test
@Ignore
public void shouldGetGeoData() throws Exception {
String ipTest = "128.101.101.101";
Map<String, Object> geoJson = new LogLine2Json().geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
如果我从资源文件夹中读取数据库文件,它会起作用。这就是为什么我有@Ignore
你不知道。您的问题体现了术语上的矛盾。资源不是文件,也不存在于文件系统中。您可以 将文件与 JAR 分开分发并将其用作 File
或 将其包含在 JAR 中并将其用作一种资源。不是都。你要拿定主意。
此外,您的整个代码看起来都无法测试。
每次在生产代码中直接调用 new 时,都会防止依赖注入;从而使测试代码变得更加困难。
重点是不要在生产代码中调用 new File()
。
相反,您可以使用为您提供 "ready to use" DatabaseReader 对象的工厂。然后你可以测试你的工厂做正确的事;并且您可以在测试此代码时模拟该工厂(return 模拟数据库 reader)。
所以,那个文件实例只是您 "testing problems" 此处的顶部。
老实说:不要先编写生产代码。做TDD:先写测试用例;您很快就会了解到,您在此处展示的此类生产代码确实很难测试。当您应用 TDD 时,您从 "test perspective" 开始,您将创建真正可测试的生产代码。
您必须使文件位置可配置。例如。通过构造函数注入它。例如。您可以创建一个仅用于测试的非默认构造函数。
public class LogLine2Json {
private static final String DEFAULT_GEO_DB = "GeoLite2-City.mmdb";
private static final String DEFAULT_GEO_FILE = "/geo/" + GEO_DB;
private final String geoFile;
public LogLine2Json() {
this(DEFAULT_GEO_FILE);
}
LogLine2Json(String geoFile) {
this.geoFile = geoFile;
}
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
File file = new File(geoFile);
DatabaseReader reader = new DatabaseReader.Builder(file).build();
// other stuff
}
}
现在您可以从该资源创建一个文件并在您的测试中使用该文件。
public class LogLine2JsonTest {
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Test
public void shouldGetGeoData() throws Exception {
File dbFile = copyResourceToFile("/geo/GeoLite2-City.mmdb");
String ipTest = "128.101.101.101";
LogLine2Json logLine2Json = new LogLine2Json(dbFile.getAbsolutePath())
Map<String, Object> geoJson = logLine2Json.geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
private File copyResourceToFile(String name) throws IOException {
InputStream resource = getClass().getResourceAsStream(name);
File file = folder.newFile();
Files.copy(resource, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
return file;
}
}
TemporaryFolder 是一个 JUnit 规则,它删除之后测试期间创建的每个文件。
您可以使用 hasToString
匹配器修改断言。如果测试失败,这将为您提供更详细的信息。 (而且你必须 read/write 更少的代码。)
assertThat(geoJson.get("lLa"), hasToString("44.9759"));
assertThat(geoJson.get("lLo"), hasToString("-93.2166"));