如何在java项目中包含一个资源文件,以便与new file()一起使用?

xkrw2x1b  于 2021-05-29  发布在  Hadoop
关注(0)|答案(3)|浏览(394)

我正在用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测试中这样做,否则我必须创建 `/geo/GeoLite2-City.mmdb` 在我的本地机器和Jenkins这不是理想的。我在想办法让我的测试在使用时通过 `new File(GEO_DB)` 而不是 `getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')` 因为

getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')

在hadoop中不起作用。
如果我运行junit测试,它会失败,因为我没有 `/geo/GeoLite2-City.mmdb` 在我的本地机器上。
有什么我能克服的吗?我只希望我的测试能够通过,而不改变要使用的代码 `getClass().getResourceAsStream` 我不能,如果/否则,因为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")));

}

如果我从资源文件夹读取数据库文件,它就会工作。这就是为什么我要忽略
zlwx9yxi

zlwx9yxi1#

你没有。你的问题在措辞上自相矛盾。资源不是文件,也不存在于文件系统中。您可以将文件与jar分开分发,并将其用作 File 或者将其包含在jar中并将其用作资源。不是两者都有。你必须下定决心。

toiithl6

toiithl62#

您必须使文件位置可配置。e、 g.通过构造函数注入。e、 你可以创建一个非默认的构造函数来进行测试。

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 匹配器。如果测试失败,这将为您提供更详细的信息(而且你必须读/写更少的代码。)

assertThat(geoJson.get("lLa"), hasToString("44.9759"));
assertThat(geoJson.get("lLo"), hasToString("-93.2166"));
jw5wzhpr

jw5wzhpr3#

另外,你的整个代码看起来是不可测试的。
每次在生产代码中直接调用new时,都会阻止依赖注入;因此,测试代码变得更加困难。
关键是不要打电话 new File() 在生产代码中。相反,您可以使用一个工厂,它为您提供一个“随时可用”的databasereader对象。然后你可以测试你的工厂做正确的事情;您可以在测试此代码时模拟工厂(以返回模拟的数据库读取器)。
因此,这一个文件示例只是您的“测试问题”的顶部。
老实说:不要先写生产代码。做tdd:先写测试用例;您将很快了解到,您在这里展示的这种产品代码确实很难测试。当您应用tdd时,您将从“测试Angular ”开始,并且您将创建真正可测试的产品代码。

相关问题