2014-11-23 50 views
0

我有一個Controller操作方法,無論發生什麼總是返回null,因爲我希望它重新加載相同的頁面。 (JSF 2.2)。單元測試只有一個結果的方法

我已經成功地使用mockito編排每條可能路徑的執行。

但是,每條路徑通常都會調用抽象方法來添加消息或調用第三方庫。

所以我可以斷言它返回null,但是不管怎麼說都是這樣。隨着發展的繼續,我看到這種模式重複。

問題:即使執行路徑,我總是測試null。

可能的解決方案,因爲我看到他們:

  1. 我很綠色的Mockito,所以有可能是別的東西我可以做 驗證第三方和抽象方法被調用。
  2. 我覺得 有些東西可能是有用的,但hacky是一種狀態標誌 某種知道什麼樣的消息剛剛添加到堆棧。哈克因爲它的唯一真正用途就是它將用於測試,就像我目前看到的那樣。

  3. 重新評估我的方法,因爲如果我在這種情況下,因爲我 代碼設計是錯誤的。

  4. 我沒有問題。保持原樣,並且確信我正在運行每條執行路徑並驗證其結果,即使認爲它是相同的。

問: 考慮什麼是已知的,哪個方向,你會考慮先嚐試解決在單元測試驗證外部內部的執行路徑的問題?還是有更好的解決方案?

在此先感謝。

與示例代碼更新解釋驗證煩惱,如果是這樣的路線我應該採取:

try { 
    account.save(); //<-- third party object i don't own, & returns void 
    addInfoMessage("All Updated!"); //<-- abstract method 
    } catch (final ResourceException e) { //<-- third party exception 
    addErrorMessage("Sorry your account could not be updated. ");//<-- abstract method 
    LOG.error("error msg"); 
    } 
    ... 
    return null; 

回答

0

看起來至少有兩個結果,因此它們覆蓋是很重要的。您可以稍微重新設計課程,以使測試變得簡單。

public abstract class YourClass { 
    protected abstract void addInfoMessage(String message); 
    protected abstract void addErrorMessage(String message); 

    public void closeTransaction() { 
    try { 
     saveAccountInternal();            /* ! */ 
     addInfoMessage("All Updated!"); 
    } catch (final ResourceException e) { 
     addErrorMessage("Sorry your account could not be updated."); 
    } 
    return null; 
    } 

    /** Package-private. Overridable for testing. */ 
    void saveAccountInternal() throws ResourceException { 
    account.save(); 
    } 
} 

你正在設計一個子類類,所以用一個子類測試:

public class YourClassTest { 
    private static class TestYourClass extends YourClass { 
    boolean saveCalled = false; 
    boolean shouldThrow = false; 
    List<String> infoMessages = new ArrayList<>(); 
    List<String> errorMessages = new ArrayList<>(); 
    protected void addInfoMessage(String message) { infoMessages.add(message); } 
    protected void addErrorMessage(String message) { errorMessages.add(message); } 

    @Override void saveAccountInternal() throws ResourceException { 
     saveCalled = true; 
     if (shouldThrow) throw new ResourceException(); 
    } 
    } 

    @Test public void closeTransactionShouldSave() { 
    TestYourClass testYourClass = new TestYourClass(); 
    assertNull(testYourClass.closeTransaction()); 
    assertTrue(testYourClass.saveCalled); 
    assertEquals(1, testYourClass.infoMessages.size()); 
    assertEquals(0, testYourClass.errorMessages.size()); 
    } 

    @Test public void closeTransactionShouldSave() { 
    TestYourClass testYourClass = new TestYourClass(); 
    testYourClass.shouldThrow = true; 
    assertNull(testYourClass.closeTransaction()); 
    assertTrue(testYourClass.saveCalled); 
    assertEquals(1, testYourClass.infoMessages.size()); 
    assertEquals(0, testYourClass.errorMessages.size()); 
    } 
} 

注意,上面解決方案不涉及的Mockito。一旦您對該測試重新設計感到滿意,您可以考慮使用Mockito自動創建子類,如this SO answer

public class YourClassTest { 
    private YourClass stubYourClass() { 
    YourClass yourClass = Mockito.mock(YourClass.test, Mockito.CALLS_REAL_METHODS); 
    doNothing().when(yourClass).addInfoMessage(anyString()); 
    doNothing().when(yourClass).addErrorMessage(anyString()); 
    doNothing().when(yourClass).saveAccountInternal(); 
    return yourClass; 
    } 

    @Test public void closeTransactionShouldSave() { 
    YourClass yourClass = stubYourClass(); 
    assertNull(yourClass.closeTransaction()); 
    verify(yourClass).saveAccountInternal(); 
    verify(yourClass).addInfoMessage(anyString()); 
    verify(yourClass, never()).addErrorMessage(anyString()); 
    } 

    @Test public void closeTransactionShouldSave() { 
    YourClass yourClass = stubYourClass(); 
    doThrow(new ResourceException()).when(yourClass).saveAccountInternal(); 
    assertNull(yourClass.closeTransaction()); 
    verify(yourClass).saveAccountInternal(); 
    verify(yourClass).addErrorMessage(anyString()); 
    verify(yourClass, never()).addInfoMessage(anyString()); 
    } 
} 

當然,在信息/錯誤消息調用中斷言「從不」可能會讓你的測試比你想要的更脆弱;這只是爲了表明使用手動或Mockito生成的子類進行測試可以爲您提供所需的所有測試。

+0

感謝您的反饋,我將嘗試在本地嘗試評估它。 – 2014-11-25 03:47:29

0

的解決方案是第一個。除了對依賴的副作用外,你的方法什麼都不做,並且可能改變被測對象的狀態。這就是那些副作用,以及你想測試的對象的新狀態。

測試新狀態很簡單。測試的依賴性副作用是使用的的Mockito的verify()方法來完成,如very first item of the official documentation解釋說:

//Let's import Mockito statically so that the code looks clearer 
import static org.mockito.Mockito.*; 

//mock creation 
List mockedList = mock(List.class); 

//using mock object 
mockedList.add("one"); 
mockedList.clear(); 

//verification 
verify(mockedList).add("one"); 
verify(mockedList).clear(); 
+0

我應該說我在其他測試中進行了驗證,並且我意識到了這一點。但在這種情況下,我不確定它是否成立,我會用一個例子更新我的問題。 – 2014-11-23 18:00:33