2016-02-12 23 views
2

我試圖添加單元測試一些傳統的代碼,它有一個傳遞給它的字符串類名稱,並創建一個實現特定處理程序接口使用Class.newInstance(String className)的對象。我可以控制我傳遞的類名,我可以獲得一個指向新處理程序對象的指針(通過調用getHandler()),並且我希望使用Mockito觀察對它的調用。如何模擬通過Class.newInstance(className)創建的對象?

我目前的解決辦法是:

  1. 創建一個新的測試類TestHandler實現接口。
  2. 該測試類是否包含一個Mockito模擬對象,該對象還可以實現該接口。
  3. 手動將所有接口方法傳遞給模擬對象。
  4. 使模擬對象可通過getMock()方法訪問。
  5. 通過對objectUnderTest.getHandler().getMock()進行verify()調用來觀察對象。

這樣的工作,但感覺有點不雅,尤其是不得不手動編寫所有傳遞方法。

有沒有更好的解決方案?

+0

你究竟想要測試什麼?當您調用某個方法時,是否創建了正確的運行時類型的實例? – kai

+0

我想驗證在新創建的對象上正在調用方法。 –

回答

1

基本上,您遇到了與嘗試使用new測試新創建的實例相同的問題; Class.newInstance(可能正確Class.forName(foo).newInstance())不會傷害你,但也不幫助你。

作爲一個便箋,您的TestHandler聽起來像一個通用的委託實現,無論如何聽起來非常有用(特別是如果您需要編寫Handler包裝器)。如果是這樣,您可能需要將其提升爲與您的生產代碼樹中的處理程序相鄰。

雖然我認識到你提到遺留代碼,但如果允許重構包含測試接縫,這將變得非常簡單。 (這裏忽略反射異常,爲了便於說明。)

public ReturnType yourMethodUnderTest(String className) { 
    return yourMethodUnderTest(Class.newInstance(className)); 
} 

/** Package private for testing. */ 
public ReturnType yourMethodUnderTest(Handler handler) { 
    return yourMethodUnderTest(Class.newInstance(className)); 
} 

你也可以提取對象的創建和測試中的替換:

/** Instance field, package-private to replace in tests. */ 
Function<String, Handler> instanceCreator = 
    (x -> (Handler) Class.forName(x).newInstance()); 

public ReturnType yourMethodUnderTest(String className) { 
    Handler handler = instanceCreator.apply(className); 
    // ... 
} 

你甚至可以只是將其解壓縮到一個方法和替換它在您的測試:

public ReturnType yourMethodUnderTest(String className) { 
    Handler handler = createHandler(className); 
    // ... 
} 

/** Package private for testing. */ 
Handler createHandler(String className) { 
    return Class.forName(className).newInstance(); 
} 

@Test public void yourTest() { 
    // Manually replace createHandler. You could also use a Mockito spy here. 
    ObjectUnderTest objectUnderTest = new ObjectUnderTest() { 
    @Override Handler createHandler(String className) { 
     return mock(Handler.class); 
    } 
    } 
    // ... 
} 

邊注:即使創建的Mockito一個名爲動態類型,你幾乎肯定無法破解它並允許你的代碼按名稱創建它。這是因爲致電mockregisters the instance within Mockito's internal state

// BAD: Unlikely to work 
@Test public void yourTest() { 
    objectUnderTest.methodUnderTest(
     mock(Handler.class).getClass().getName()); 
    // ... 
} 
+0

謝謝,這似乎是一種很好的方式,一旦我得到單元測試,然後允許我刪除我所關心的模擬對象封裝,就可以向前移動並重構。 –

相關問題