2017-02-20 102 views
0

我需要單元測試一個方法,並且我想嘲笑這個行爲,以便我可以在方法中測試代碼的必要部分。在公共方法中偵聽私有方法返回的對象

爲此,我想訪問由我嘗試測試的方法內的私有方法返回的對象。我創建了一個示例代碼來給出我想要實現的基本概念。

Main.class

Class Main { 
    public String getUserName(String userId) { 
    User user = null; 
    user = getUser(userId); 
    if(user.getName().equals("Stack")) { 
     throw new CustomException("StackOverflow"); 
    } 

    return user.getName(); 

} 

private User getUser(String userId) { 
    // find the user details in database 
    String name = ""; // Get from db 
    String address = ""; // Get from db 
    return new User(name, address); 
} 
} 

測試類

@Test (expected = CustomException.class) 
public void getUserName_UserId_ThrowsException() { 
    Main main = new Main(); 
    // I need to access the user object returned by getUser(userId) 
    // and spy it, so that when user.getName() is called it returns Stack 
    main.getUserName("124"); 
} 

回答

0

可以使用PowerMock的mockPrivate但我不建議這樣做。 如果你有這樣的問題,通常意味着你的設計不好。 爲什麼不保護該方法?

+0

我不會說確保提供的方法是提高代碼質量,而是正確地模擬「從數據庫中獲取」部分。 – skomp

+0

@Egor我不確定我是否完全遵循,但是如何模擬私有方法幫助我在用戶對象上創建間諜? – bharathp

1

只有訪問私有兩種方式:

  1. 使用反射
  2. 擴大範圍
  3. 也許等待的Java 9使用新範圍的機制?

我會將範圍修飾符從私有變爲包範圍。重構使用反射不穩定。使用PowerMock等助手無關緊要。他們只是減少反射周圍的鍋爐代碼。

但是最重要的一點是你不應該在惠特盒測試中測試太深。這可以使測試設置爆炸。嘗試將你的代碼分成更小的部分。

用戶對象所需的方法「getUserName」的唯一信息就是名稱。它會驗證名稱並引發異常或將其返回。所以在測試中不應該引入一個用戶對象。

所以我的建議是你應該將代碼retreiving從用戶對象名稱到一個單獨的方法,並使此方法包的範圍。現在不需要模擬一個用戶對象就是主對象。但是該方法只有最少的信息才能正常工作。

class Main { 

    public String getUserName(String userId) { 
     String username = getUserNameFromInternal(userId); 
     if (userName.equals("Stack")) { 
      throw new CustomException("StackOverflow"); 
     } 
     return user.getName(); 
    } 

    String getUserNameFromInternal(String userId) { 
     User user = getUser(userId); 
     return user.getName(); 
    } 

    ... 

} 

測試:

@Test (expected = CustomException.class) 
public void getUserName_UserId_ThrowsException() { 
    Main main = Mockito.mock(new Main()); 
    Mockito.when(main.getUserNameInternal("124")).thenReturn("Stack"); 
    main.getUserName("124"); 
} 
+0

我的帖子中的代碼只是一個示例。想象一下getUserName以多種方式使用User對象的情況。比方說,我們在getUserName中調用了大約15個User對象的方法。但我只想嘲笑其中一個方法調用的行爲。我會怎麼做呢? – bharathp

+1

@bharathp如果** one **方法對某個對象進行另外15次調用,那麼這又是一個糟糕的設計,有人不明白OO是如何使用的;-) – GhostCat

+0

如果在getUserName中調用15個方法這不會影響測試方法。但是@GhostCat提到這是一個糟糕的設計指標,你應該質疑,如果你違反了單一責任原則。但將這種方法分塊並將其置於測試中仍然是潛在重構的第一步。 – oopexpert

1

您的問題,您的私有方法中調用new

而答案是不轉向PowerMock;或者改變該方法的可見性。

合理的答案是「提取」依賴於「給我一個用戶對象的東西」到它自己的類中;並將該類的一個實例提供給「Main」類。因爲那樣你就可以簡單地嘲笑那個「工廠」對象;並讓它做你想做的任何事情。

含義:你當前的代碼很難測試。與其解決由此造成的問題有關,您花時間學習如何編寫易於測試的代碼;例如通過觀看這些videos作爲起點。

鑑於您的最新評論:當您正在處理遺留代碼時,您真的很想使用PowerMockito。要理解的關鍵部分:你不要「嘲笑」那種私人方法;你寧願嘲笑撥打new User()的電話;如概述here

+0

您能否詳細說明您的第三段。不幸的是,我測試的代碼是遺留代碼,我不會花時間更新它。很多感謝鏈接到播放列表。 – bharathp

+0

我的意思是:如果你會在這裏寫你自己的代碼;那麼你可能想退後一步並學習......如何以「更好」的方式來實現這一點。但是,在處理現有的代碼時您無法觸摸,那麼PowerMock是您在那裏唯一的答案。看到我更新的答案和第四段;-) – GhostCat

+0

我不認爲這是遺留代碼和「新」代碼的問題。在這種情況下,問題是白盒測試或黑盒測試。問題是:你關心控制流程還是關心輸入參數而不知道HOW如何產生某些結果?如果你關心控制流程,你會關心代碼質量。如果你只想確定一個對象方法產生符合規範的結果,那麼當每個控制流成爲「實現細節」時,就沒有真正的強制執行代碼質量的句柄。 – oopexpert

相關問題