2017-09-29 117 views
1

我正在經歷一個困難的時間,弄清楚如何解決單元測試問題。我很少或沒有'單元測試'的經驗。我試圖只在絕對必要的情況下用最小的更改來更改classUnderTest如何Java單元測試一個複雜的類

我使用的是JUnit 4,我願意嘗試使用MockitoJMockit或任何其他有助於進行高效且有效的單元測試的庫。我正在爲數據庫使用JDBC

問題:

classUnderTest,我訪問靜態數據庫。

SpecializedDao specializedDao = SpecializedDao.getSpecializedDao(); 
ObjectX objectResult = specializedDao.currentObjectXByTimestamp(x, x, x, x, x, x); 

我試圖用一個有效的配置和一個無效的配置做單元測試用例。解決這個問題的正確方法是什麼?

一些可能解決方案,我已經研究有:

  1. 不知怎麼傳遞,或者「注射」假ObjectXResult類_對象
  2. 模擬數據庫(如果甚至有可能與靜數據庫引用,使用單獨的單元測試servlet引用一個假的SpecializedDao類?)
  3. 使用MockitoinjectMocks,間諜,當(方法調用參數)然後返回結果,或一些其他實現Mockito規定。
  4. 開放給任何其他解決方案。

問題:

classUnderTest,我使用的另一項複雜的處理類class2,做了很多工作,像這樣:

result = class2.execute(x, x, x, x, x, x); 

類class2做處理的東西,並返回結果ENUM或一些例外。我該如何處理這個問題,並在classUnderTest上保留此特定單元測試的範圍class2訪問數據庫,並做了很多工作,這就是爲什麼它是它自己的類,但我認爲最後三個測試用例依賴於處理以徹底測試class classUnderTest

感謝您對我的支持,我儘可能以儘可能清晰的方式提出問題。

+1

這聽起來像你有幾層在這裏測試。在我的組織中,我們使用像服務層(稱爲dao層)和dao層(實際上調用jdbc調用)的體系結構。就單元測試而言,要考慮按班級分開你的班級。因此,對於執行實際「業務邏輯」的服務層,您可以使用EasyMock等模擬DAO調用。這將允許您將特定邏輯與服務層隔離開來。對於你的DAO層,你可以考慮使用類似嵌入式數據庫(DERBY)的東西來測試 – Jason

+0

如果你絕對不能改變類,PowerMockito允許你模擬靜態方法。但我傾向於認爲使用PowerMockito作爲設計失敗的必要性 - 可測試性應該首先是設計標準之一。 –

回答

1

我的建議,如果你在乎開發好的有用測試,是模擬數據庫或其他複雜的類,你的代碼可以與互動。

相反,認爲所測試你的代碼是爲了實現特定的業務場景,並寫現實(即沒有用廉價替代真正的類/組件 - 而且往往不正確 - 他們的仿製品)和有意義的(從實際需求角度)測試每個場景。

這就是說,如果你仍然想嘲笑那個DAO類,它可以使用JMockit如下進行:

@Test 
public void exampleTest(@Mocked SpecializedDao mockDao) { 
    ObjectX objectResult = new ObjectX(); 

    new Expectations() {{ 
     mockDao.currentObjectXByTimestamp(anyX, anyY, anyZ); result = objectResult; 
    }}; 


    // Call the SUT. 
} 

anyX等並不是實際JMockit參數匹配字段,當然 - 真正的是anyInt,anyString,any等)

3

在任何情況下,你都需要使用類似Mockito的東西,所以我會假設你有這個。

第一個問題:

測試代碼,分爲靜態調用可以是一個痛苦。你可以包含一些字節碼操作庫,如PowerMock,但在我看來這並不值得。你可以做的是將靜態調用放在包本地方法中,然後使用spy對其進行存根。喜歡的東西:

//in class under test: 
SpecializedDato getSpecializedDao() { 
    return SpecializedDao.getSpecializedDao(); 
} 

//in test: 
import static org.mockito.Mockito.*; 
//... 
final SpecializedDao daoMock = mock(SpecializedDao.class); 
final ClassUnderTest classUnderTest = spy(new ClassUnderTest()); 
doReturn(daoMock).when(classUnderTest).getSpecializedDao(); 

問題二:

如果你發現你的測試變得非常複雜,這可能是由於在測試太複雜的類。看看您是否可以將功能提取到其他較小的類中。然後,您只需驗證是否正在調用這些較小的類。

+0

好的,我會盡力實現這個。你打算輸入SpecializedDao而不是SpecializedDato嗎?或者是數據訪問測試對象,SpecializedDao的模擬? – weteamsteve

+0

是的..更新的答案.. – Tobb

3

一個很好的規則是永遠不要連接到JUnits的外部源。 任何事情,比如數據庫連接,你都可以用Mockito來嘲笑他們。

使用Mockito,你可以模擬你不需要測試的所有課程。在你的情況下,你可以嘲笑那些沉重的課程並返回預期的結果。

+1

如果Mockito不適合你,也有JMockit –

1

在我的辦公室,我們通常使用一些解決方案來使複雜的類更具可測試性。

最不可取的是隻有一個能夠在沒有修改被測試類的情況下工作 - 這是一種嚴重的嘲諷。你嘲笑你課外的一切。如果你在課堂上有一個靜態成員,你可以在開始之前將它設置爲模擬反射(可能是你的問題的答案)。

這種方法很難並且很脆弱,但如果你不能修改被測試的類,它幾乎是唯一可行的方法。

一個小異常是一個稍微不同的模擬版本 - 擴展了被測試的類並覆蓋了你的類與之交互的所有東西。這對於靜態效果並不總是有效,但如果你的類是使用getter設計的,那麼效果會很好。

如果你可以修改你的類,我們使用的一些技巧,以幫助:

1)避免靜態所有的時間。如果你必須有一個單身人士,請確保它可以通過使用注射框架或自己構建一個迷你注射框架進行測試(一個靜態的預填充地圖應該是一個好開始)

2)我們把很多我們純函數中的業務邏輯,我們稱之爲包含所有這些邏輯「規則」類的類。我們的規則通常可以直接追蹤到需求,並且它們不提供任何自己的數據,所有的數據都被傳入。它們通常由包含相關數據和其他代碼的OO外觀調用。

3)減少類的複雜性和相互作用。將數據庫訪問與計算分開。看看每個班級,並確保它確實做到了一件事。這不會直接幫助,但您會注意到,如果遵循此規則,編寫測試會更簡單。

1

在模擬框架上沒有針對性的解決方案,處理僅涉及CuT最小改變的靜態的標準模式是提取靜態方法以坐在接口後面,並且最初訪問靜態方法的類現在訪問方法在接口上。這個接口的一個實例被注入到CuT中,當不進行測試時,它是一個小包裝類,它直接將代理直接轉換爲原始靜態方法。

現在你有一個接口可以使用,你可以通過setter或構造函數注入來注入這個接口的實例,Bob是你的叔叔。

這個標準的技巧確實需要一些額外的「東西」 - 一個接口和一個實現接口的小包裝類,它委託給靜態接口,以及一些代碼或DI框架接線,以實現在生產部分的類通過構造函數或setter注入到CuT中的代碼。

然後在你的單元測試中,你有一個鉤子來注入一個模擬或者手工創建的存根,或者任何你想要的測試樣式。

這是一個非常常見的模式,當處理一個第三方庫超出你的控制,喜歡使用靜態。