2016-04-21 64 views
1

我正在使用Java爲Pig編寫一個UDF。它工作正常,但豬不給我選擇分開環境。我的Pig腳本正在做的是從IP地址獲取地理位置。如何在Java項目中包含資源文件以便與新的File()一起使用?

這是我在地理位置部分的代碼。

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。我試圖同時使用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"))); 

} 

這要是我讀了資源文件夾中的數據庫文件,它的工作原理。這就是爲什麼我有@Ignore

回答

1

此外,你的整個代碼看起來很不可測。

每次在生產代碼中直接調用新的時,都會阻止依賴注入;從而使你更難測試你的代碼。

的一點是你的產品代碼中不調用​​。 相反,您可以使用工廠爲您提供「隨時可用」的DatabaseReader對象。然後你可以測試你的工廠做正確的事情;並且您可以在測試此代碼時模擬該工廠(以返回模擬數據庫讀取器)。

所以,這一個文件實例是你的「測試問題」這裏只是頂部。

老實說:不要先寫產品代碼。做TDD:先寫測試用例;你很快就會知道你在這裏展示的這種生產代碼真的很難測試。當你應用TDD時,你從「測試視角」開始,你將創建真正可測試的產品代碼。

+0

我同意你的觀點,但不幸的是。我曾嘗試過依賴注入,但Hadoop對於如何將代碼分發到不同的機器和JVM有點奇怪。有時候它會無緣無故地炸燬。 – toy

+0

是的,「無理由的炸彈」可能會翻譯爲:你的代碼是越野車,但有一個競賽條件;因此它'只是偶爾'炸彈',所以更好的理由是試圖通過單元測試來獲得「全面覆蓋」,而且你知道,依賴注入(可以)歸結爲一個僅用於測試的構造函數更多的參數,如果這會'炸彈'你的應用程序......那麼,正如所說:也許有一個很好的理由。 – GhostCat

0

你不知道。你的問題體現了矛盾。資源不是文件,也不在文件系統中。您可以要麼從JAR單獨分發的文件,並把它作爲一個File將其包含在JAR並使用它作爲一種資源。不是都。你必須下定決心。

1

您必須使文件位置可配置。例如。通過構造函數注入它。例如。您可以創建一個僅用於測試的非默認構造函數。

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")); 
相關問題