2012-02-15 22 views
1

所以,我已經實現了我的業務類,我通過構造函數傳入所有依賴關係,因此我可以將它們嘲笑出來並輕鬆地進行單元測試。這到目前爲止效果很好,但是之後,我需要從這些對象中創建一個對象圖。爲此,我使用了一個靜態工廠(我不能使用DI框架,令人傷心)。例如:如何測試/模擬這個靜態工廠(用於圖形構造)?

public class FooBar { 
    public FooBar(Foo foo, Bar bar) { 
     this.foo = foo; 
     this.bar = bar; 
    } 
} 

public class Foo { 
    public Foo() {} 
} 

public class Bar { 
    public Bar(Foo foo) { 
     this.foo = foo; 
    } 
} 

public class GraphFactory { 
    public static FooBar newFooBar() { 
     Foo foo = new Foo(); 
     Bar bar = new Bar(foo); 

     return new FooBar(foo, bar); 
    } 
} 

所以,我不能夠真正檢驗GraphFactory(不能嘲笑的依賴關係),這是還挺好的(沒有太多的工作都是在這裏完成)。但是,如果構建圖更復雜,即涉及查找某些屬性,執行JNDI查找等,該怎麼辦?

我還不應該爲此編寫單元測試嗎?我的課程設計是否可能破裂?單元測試的目的不是孤立地測試類嗎?

+0

提供編譯示例如何?我懷疑'公共靜態newFooBar(){...}'實際上編譯... – Romain 2012-02-15 09:16:49

+0

@Romain Sry,修復了。 – helpermethod 2012-02-15 09:20:54

回答

2

這個設計沒有錯,它是完全可測試的。如果我們專注於工廠的單一責任 - 它負責組裝對象圖 - 對此的測試非常簡單:

  1. 設置工廠及其先決條件。
  2. 調用工廠方法。
  3. 斷言對象構建您的要求:默認值被設定等

在上面的例子中,FOO,酒吧和foobar的是測試的結果,並且不需要被嘲笑。

如果對象圖的組裝更復雜,並且您需要額外的服務來獲取數據並檢查應用程序設置,那麼猜猜會發生什麼?這些依賴關係通過其構造函數傳入工廠。您應該嘲笑這些依賴關係,以便將工廠與依賴項的依賴關係隔離開來。你的工廠的消費者將通過他們的建造者收到一個完全有線的工廠;或者工廠在應用程序啓動時進行組裝,並作爲單件等提供。

這是DI的工作原理。這聽起來很奇特,因爲現在你不得不擔心工廠是如何創建的(以及誰創建了這個對象等,一直都是烏龜),這是完全自然的反應。

這就是爲什麼我們有DI框架來組裝複雜的對象圖。在精心設計的DI應用程序中,沒有什麼應該知道圖表是如何組裝的。類似於這個設計,沒有任何關於DI框架的知識。

該規則的唯一例外是...(鼓式卷)...工廠對象!

如果要解析的對象在其構造函數中使用DI,則大多數DI框架都會將DI容器注入到對象中。這極大地簡化了工廠的管道,並且滿足了我們的設計原則:除了我們的工廠,沒有人知道如何構建我們的對象,並且我們已經將DI容器的知識封裝到負責對象組裝的應用程序的關鍵區域。

噢。現在,如果你還在繼續,這是如何改變我們的測試?它不應該,至少不要太多:第1步稍微改變,在那裏你用模擬對象填充DI容器,然後用DI容器構建工廠。

作爲一個品味問題,有些人喜歡在測試過程中使用自動嘲諷DI容器,當請求一個項目時會自動生成模擬依賴關係。這可以消除大部分的痛苦。

+0

+1謝謝你這個啓發性的答案!我真的學到了很多東西!這就是爲什麼我喜歡SO ...有時候你會收到答案,它會在你的腦海中設置一個開關:-)。 – helpermethod 2012-02-16 09:32:59

1

你應該看看依賴注射劑,它可以讓你在正確使用時擺脫工廠。我推薦你這個視頻作爲介紹,它幫了我很多: http://code.google.com/p/google-guice/

這是一個來自google的圖書館Guice的介紹,但它會幫助你理解DI。然後你會看到如何用註釋替換所有那些「新」,讓圖書館爲你做所有的工作。有些人會推薦Sring,但我覺得Guice對初學者更友好。我只是希望這不會觸發一個吉斯VS春再次討論:d

+0

我使用Guice regulary,它可以很容易地解決我的問題,但可惜我不能在我目前的項目中使用它。仍然是一個很好的建議:-)。 – helpermethod 2012-02-15 09:25:08

+1

我建議你閱讀食譜14.5從Junit Recipes(J.B. Rainsberger)書中測試一個對象工廠。這幾乎是@DavidWallace所說的,但是有不同的表述。 – Chirlo 2012-02-15 11:46:49

+0

看起來像一本非常好的書,我可能會購買它。 – helpermethod 2012-02-15 18:12:28

2

如果newFooBar方法不得不更加複雜,你可以重構了一切,除了爲創造FooBar成包,私人初始化方法。然後爲初始化方法編寫測試。

public class GraphFactory { 
    public static FooBar newFooBar() { 
     Foo foo = new Foo(); 
     Bar bar = new Bar(foo); 
     FooBar toReturn = new FooBar(foo, bar); 
     initialise(toReturn); 
     return toReturn; 
    } 

    static void initialise(FooBar toInitialise){ 
      // some stuff here that you can test 
    } 
} 
+0

+1太糟糕了,我只能對此舉一次:-)。我一直在努力解決這個問題,但從未想過這樣的事情:-)。 – helpermethod 2012-02-15 09:42:26

2

你不能模擬或存根靜態方法調用,但你可以編寫一個單位測試的工廠沒有嘲笑。

但是,爲什麼使用靜態工廠方法?如果要以靜態方式訪問工廠,可能會更好地將工廠存儲在靜態變量中。

相關問題