2011-05-12 57 views
3

我是新來的單元測試。有看起來像一個方法:你如何爲需要很長時間的方法進行單元測試?

public Image getImage(String url) { 
    Document pageSource = fecthSource(url); 
    Image myImage = parseHtmlToImage(pageSource); 
    return Image; 
} 

我寫一個單元測試:

@test 
public void getRightPicture() { 
    Image img = imageFetcher.getImage("http://www.123.com"); 
    assertEquals(img.sourceUrl, "http://www.123.com/456.png"); 
    Image img = imageFetcher.getImage("http://www.abc.com"); 
    assertEquals(img.sourceUrl, "http://www.abc.com/def.png"); 
} 

但是,如果它需要很長的時間來訪問互聯網。我通常希望使用本地HTML文件來測試此方法,但有時會測試Web版本。

有什麼建議嗎?

+0

我知道我可以傳遞一個本地url作爲getImage的參數,但這是一個特例。例如,也許我需要使用預先計算的數據來代替測試的慢速算法。 – 2011-05-12 18:19:52

回答

3

您的單元測試不需要驗證互聯網的工作。您應該提供一些模擬HTML文件並指向那些用於測試的文件。既然你正在處理一個URL,你應該能夠指定一個像"file:///path/to/test.html"這樣的路徑來代替網址。

+0

我想要但是如何?這意味着我必須公開paresHtmlToImage()? – 2011-05-12 18:23:50

+0

我更新了答案,您應該能夠指定URL的文件路徑。 – WhiteFang34 2011-05-12 18:25:54

+0

看到我對OP的評論。爲了方便,我使用了URL示例 – 2011-05-12 18:29:13

1

這裏有兩個選擇 -

  1. 本地文件肯定是一個做事的更快的方式;另外,你可以做的是創建一個模擬對象(請參閱Mockito或Easymock促進模擬),以便它可以模擬您嘗試測試的類中的fetchURL方法;

  2. 在其他時候,當你想從web獲取URL時,它會變得不僅僅是單元測試,因爲你正在外部移動測試依賴關係。在這種情況下,您必須處理副作用,即網絡延遲,資源可用性等。此集成測試的一個優化是在多個測試依賴於該資源時緩存資源。這樣你就不需要每次都通過網絡進行訪問。

+0

我會在1中看到你提到的庫。但是我不明白2.是什麼意思,我應該在單元測試中測試本地版本,但在集成版本中測試網絡版本? – 2011-05-12 18:27:37

+0

是的 - 你說得對。另外,如果你有多個測試測試fetchURL(...)的方法,我建議緩存結果。 – kuriouscoder 2011-05-12 18:39:31

3

簡答:依賴倒置。

稍長回答:

問題是可能涉及到什麼fetchSource所做的事。沒有看到細節,這個答案將不得不有點模糊。我假設fetchSource調用了一些實際執行檢索的內容,並且這個過程的詳細信息並不是你真正想要測試的內容。我還假設你想在寫「fecht」的地方寫「抓取」,但對答案並不特別重要。

您想抽象掉fetchSource的具體位,以便您可以專注於測試對您來說重要的事情(parseHtmlToImage?)。有多種方法可以做到這一點:

  • 構造您imageFetcher與存根實現,通過它您實際檢索URL的界面,在傳遞通過構造(反向相關性);你的存根將扮演「互聯網」的角色並返回一個罐頭響應。
  • 如果您無法創建接口的存根實現,請引入一個適配器接口,其中包含使用當前用於檢索的接口的生產實現以及用於測試的存根實現。

這是編寫可測試代碼的相當標準的技術(查找依賴性反轉原理)。

+0

>通過構造函數傳入 什麼是「它」?網址?該對象實現了存根方法? (你能舉幾個例子嗎?我讀了wiki上的DIP,但不知道如何在這種情況下使用它) – 2011-05-12 18:36:46

+0

對不起,延遲;仍然不習慣StackExchange UI來跟蹤新的評論。在這種情況下,「它」將是生產代碼中接口的生產實現,以及測試代碼中的接口的存根實現(在這種情況下,該接口抽象出實際獲取URI的細節)。 – 2011-05-16 19:40:07

1

爲了擴大對依賴注入的答案以上:

所以,你必須:

class ImageFetcher { 
    public Image getImage(String url) { 
     Document pageSource = fetchSource(url); 
     Image myImage = parseHtmlToImage(pageSource); 
     return Image; 
    } 
    private Document fetchSource(String url) { 
     // ... Network IO etc 
    } 
    private Image parseHtmlToImage(Document pageSource) { 
     // Parsing logic 
     // Network IO 
     return image; 
    } 
} 

的第一個問題是「是否所有真正屬於‘ImageFetcher’IMO fetchSource沒有這(。對我來說)聽起來象是一個不同的「網絡utils的」類會怎麼做。我還從ImageFetcher到utils的類移動圖像的獲取。

class ImageFetcher { 
    private WebUtils webUtils; 
    public ImageFetcher(webUtils webUtils) { 
     this.webUtils = webUtils; 
    } 


    public Image getImage(String url) { 
     Document pageSource = webUtils.fetchSource(url); 
     Image myImage = parseHtmlToImage(pageSource); 
     return Image; 
    } 

    private Image parseHtmlToImage(Document pageSource) { 
     imageURL = // Parsing logic 
     image = webUtils.fetchImage(imageURL); 
     return image; 
    } 
} 

現在圖像擷取我主要關心的是確定要使用哪個圖像。它不再需要關心你如何從網絡上獲取該圖像。 WebUtils可以被模擬進行單元測試。

@test 
public void getImage_ReturnsBannerImage() { 
    Image expected = // ... 


    String html = "<html><img src="fred"/>...</html>"; // Keep this short and simple. 
    mockedWebUtil.fetchSource(Any.String).returns(html); 
    mockedWebUtil.fetchImage("fred").returns(expected); 

    var subject = new ImageFetcher(mockedWebUtil); 
    var actual = subject.getImage("blah"); 

    Assert.AreEqual(expected, actual); 
} 
相關問題