2012-06-17 82 views
2

讓我們假設我已經測試這個Java ClassA依賴於難以實例ClassB單元測試最佳實踐:空構造函數還是模擬對象?

public class ClassA 
{ 
    public ClassA() 
    { 
     String configFile = "config_file.xml"; 

     // I have to pass configFile to instantiate ClassB. 
     // And for example if configFile does not exists in the testing machine? 
     // Wouldn't it be easier to have an empty constructor for classB to test ClassA? 
     ClassB classB = new ClassB(configFile); 
    } 

    // ... 
} 

ClassB

class ClassB 
{ 
    ClassB(String configFile) 
    { 
     // Set up configs. 
    } 

    // ... 
} 

它是不好的做法,建立內部classB一個空的構造僅測試目的? 或者爲此改寫一個簡化的模擬ClassB更好嗎?

+2

對於IoC和依賴注入來說,這看起來像是您的合適場景。這樣ClassA只會聲明它「需要」ClassB的一個實例,並且您的IoC容器將連接並構建所需的依賴關係。並且在你的單元測試的情況下,你可以配置一個ClassB的模擬實例來測試ClassA。 您是否聽說過IoC和DI? –

+0

謝謝,我會看看他們。 – mt22

回答

5

對於IoC和依賴注入來說,使用類似SpringGuice的東西看起來像是您的情況。

那樣ClassA只會聲明它「需要」一個ClassB的實例,並且你的IoC容器會連接並構造所需的依賴關係。然而,如果你不想得到一個框架,你可以通過從ClassB中提取一個接口契約來解決它,並且ClassA依賴於它,並且在它的構造函數中接收該契約的一個實現。

例如:

interface SomeContract { 
    void someBehavior(); 
} 

ClassB實現:

class ClassB implements SomeContract { 
{ 
    ClassB(String configFile) 
    { 
     // Set up configs. 
    } 

    void someBehavior() { 
     // ... 
    } 
} 

和使用它,像:

public class ClassA 
{ 
    private SomeContract implementation; 
    public ClassA(SomeContract implementation) 
    { 
     this.implementation = implementation; 
    } 

    // ... 
} 

這樣一來,在你的單元測試,你可以通過您在測試中確定的實例,如Mock,並且您可以相應地進行測試,而無需修改代碼爲ClassB

底線是,你應該儘量避免創建額外的構造函數等,僅用於測試目的。

1

關於我的看法,如果傳遞的配置文件丟失或不存在,我將提供給B類的選項,以使用默認配置啓動。

編輯: 衆所周知的工件,如Weld/CDI,Hibernate驗證器和Arquilluan,也提供了默認配置和空配置文件。如果沒有任何提及,將應用默認值。

5

實際上,使用無參數構造函數並使用new關鍵字創建依賴關係會使您的類無法測試。由於您無法從構造函數中注入ClassB依賴項,因此您絕不可以嘲笑它並隔離測試中的代碼。所有的單元測試都必須測試這兩個類,這使得它成爲一個不好的單元測試。相反,您應該添加每個依賴項作爲構造函數參數。

這裏有一個更好的方法:

public class ClassA 
{ 
    // use an abstraction instead of a concrete class 
    private IClassB _classB; 

    public ClassA(IClassB classB) 
    { 
     _classB = classB; 
    } 
} 

這樣你可以嘲笑IClassB,你不必擔心任何ClassB的實現細節。