2013-06-25 70 views
2

我有一個相當複雜的方法,我想測試行爲(使用Mockito和JUnit)。此方法將一個對象(我們稱其類型爲State)作爲輸入,並應考慮幾個不同的狀態變量來決定其輸出。調用mockito中的模擬對象

作爲一個例子,考慮下面的說明書(sState類的模擬):

  1. 如果s.varOne被設置,返回其值。
  2. 否則,如果設置了s.varTwo,則返回該值。
  3. 否則,如果s.varThree設置,調用s.update(s.varThree)然後返回s.varOne,現在將有一個值(即使它沒有在階段1)
  4. 否則,拋出一個錯誤。

爲了測試案例3得當,我想建立s對象,以便s.varOnes.varTwo都沒有設置,開始用,但如果(且僅當!)的SUT調用s.update(s.varThree),再經過那s.varOne返回的東西。

有沒有一種很好的方法來在Mockito中設置這種行爲?

我已經考慮爲s.varOne設置一些返回值鏈,然後驗證調用的順序是否與輸出的順序相對應(以及當然,sut的返回值是否正確) ,但這感覺很髒;如果我然後改變方法來計算其返回值的方式,它調用s.varOne不同的次數,但不會改變它的輸出,那麼即使功能相同,測試也會失敗。

我的理想解決方案是,我可以爲模擬對象添加一些「延遲」設置,當該對象調用s.update()方法時運行該對象,但我無法找到實現該方法的方法。

+2

關於您的測試的可疑之處在於您的第三個示例中您的模擬成爲測試中的系統。你可以讓模擬返回任何你想要的,但你想測試**模擬**返回一定條件下的東西。難道你不能重組你的代碼,以便你的SUT是一件事而不是1.x事情嗎? –

+0

這聽起來像你在同一個測試用例中有多個斷言一樣。你能否把你的測試案例分解成更小的測試用例,每個測試用例只測試一個條件?你不應該在模擬中需要任何條件邏輯。如果您正在測試的代碼將模擬調用兩次,並且每次都要求不同的返回值,那麼只需將多個值傳遞給您的'thenReturn'調用(如我的示例http://stackoverflow.com/questions/8088179/using -mockito與 - 多呼叫到所述-相同方法-用最同參數/ 8395685#8395685)。 –

+0

@DavidWallace:如問題所述,我不想創建一個返回值鏈,因爲這會使得測試依賴於實現而不僅僅依賴於合約(即「必須獲得varOne'兩次,然後調用'update()',然後重新獲得它,否則測試將失敗,即使合同 - 也許 - 仍然是全滿的)。 –

回答

2

你有幾個選擇來模擬狀態變化,這是一個很好的選擇和更好的選擇。最好的選擇,如上面的tieTYT筆記,僅僅是爲了解開State的總合同:State是否可變是否有意義,並且具有不是簡單設置者的自變異方法?

好的選項(有時候)是創建一個一次性的子類,或者更好的是一個接口實現State

@Test public void yourTest() { 
    SystemUnderTest sut = createSystemUnderTest(); 
    State state = new State() { 
    @Override public void update() { 
     this.varOne = 42; 
    } 
    } 
    // rest of your test 
} 

在這一點上,你可能根本不需要Mockito。不過要注意的是:如果國家有副作用或難以實例化,這可能會有點棘手。你可以提取一個接口,這將需要你把你的領域包裝在getter中;你也可以使State成爲一個命名的抽象類,然後通過mock(MyAbstractState.class, CALLS_REAL_METHODS),但是當你認爲沒有初始化器實際運行在假實例上時,這會變得特別多毛,因此這些字段全部爲零或空。如果不是那麼簡單,不要浪費你的時間來迫使一個方形的釘子進入一個圓孔。

一個更常見的mock模式是使用自定義的Answer,其中您可以執行任意代碼以犧牲類型安全性爲代價。這裏有一個例子,假設update是無效的,並varThree是一個整數:

@Test public void yourTest() { 
    SystemUnderTest sut = createSystemUnderTest(); 
    final State s = Mockito.mock(State.class); 
    doAnswer(new Answer<Void>() { 
    @Override public Void answer(InvocationOnMock invocation) { 
     int actualVarThree = (int) invocation.getArguments()[0]; 
     assertEquals(EXPECTED_VAR_THREE, actualVarThree); 
     s.varOne = actualVarThree; 
     return null; 
    } 
    }).when(s).update(anyInt()); 
    // rest of your test 
} 

注意的參數數組是Object[],所以演員是必要的,稍有危險,但你可以做出種種斷言並在您調用模擬方法時同步修改。

希望有幫助!

+0

答案技巧就是我最後的結果,'State'是一種不受我控制的類型,帶有一堆副作用等,所以「不幸的是,「最好」的方法是不可用的爲廣泛的答案anks! –