簡體   English   中英

沒有依賴注入的單元測試

[英]Unit testing without dependency injection

來自 Spring in Action 的代碼:

public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;
    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}

public class BraveKnight implements Knight {
    private Quest quest;
    public BraveKnight(Quest quest) {
        this.quest = quest;
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}


public class BraveKnightTest {
    @Test
    public void knightShouldEmbarkOnQuest() {
        Quest mockQuest = mock(Quest.class);
        BraveKnight knight = new BraveKnight(mockQuest);
        knight.embarkOnQuest();
        verify(mockQuest, times(1)).embark();
    }
}

我理解依賴注入的使用,它允許我們在不修改依賴代碼的情況下切換實現。

這本書說“編寫單元測試非常困難......”。

但是,我無法理解沒有依賴注入的單元測試將是多么困難! 我的直覺拒絕合作!

您能否開始為“DamselRescuingKnight”類和任何其他更好的示例類(沒有 DI)編寫 junit/單元測試,讓我意識到 DI 使單元測試更容易的點/階段?

當您嘗試測試DamselRescuingKnight時,上面示例中的困難就出現了。 假設,你想測試那個(見下文)

public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;
    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();
    }
    public void embarkOnQuest() {
        quest.embark();
    }
}


public class DamselRescuingKnightTest {
    @Test
    public void knightShouldEmbarkOnQuest() {
        DamselRescuingKnight knight = new DamselRescuingKnight ();
        knight.embarkOnQuest();
        // now what?            
    }
}

你怎么能確定 Knight.embarkOnQuest() 確實做了什么? 答案是你不能,因為你不能訪問它內部使用的任務實例。

現在為了能夠測試這樣的類,您需要向 Knight 添加一個getQuest()方法,然后向 Quest 添加一個isEmbarked()方法。 也很公平地說,這個例子很簡單,因為騎士只調用了沒有參數的任務,沒有別的。 如果騎士會與任務互動並從鐵匠那里獲得一些武器,那么您也需要以某種方式允許訪問。 你可能可以做所有的樣板來完成它。 但是假設您正在將參數傳遞給鐵匠 - 您如何確保傳遞的參數是正確的? 或者你如何確保騎士去任務之前拿到他/她的武器?

這就是依賴注入發揮作用的地方。 您可以創建模擬(通過使用模擬框架或通過實現您自己的模擬),以便您可以驗證您的騎士是否執行了預期的操作。

問題當然是quest變量。 您想以某種方式檢查embark()方法是否被調用。 無法用模擬實例替換它,這非常困難。

如果變量是protected而不是private ,測試用例可能會因為位於同一個包中而覆蓋它。

您還可以使用面向方面的編程來替換變量。

但最簡單的是,如果代碼是從一開始就使用依賴注入編寫的。

您要求查看如何使用 AOP。 以下是一個 AspectJ 切入點示例,您可以在單元測試中使用該切入點將RescueDamselQuest實例替換為一個名為MockRescueDamselQuestRescueDamselQuest實例(抱歉,如果我沒有完全正確地MockRescueDamselQuest語法,自從我使用 AspectJ 以來已經有一段時間了):

aspect MockRescueDamselQuestInstantiations {
    RescueDamselQuest around (): call(RescueDamselQuest.new()) {
        return new MockRescueDamselQuest();
    }
}

這將捕獲RescueDamselQuest任何實例化(即調用new RescueDamselQuest() )並返回一個MockRescueDamselQuest對象。

鑒於這需要更多的布線,我強烈建議改用依賴注入!

當我在 Spring in Action 中閱讀本文時,這也讓我感到困惑。 閱讀以上答案后,我想補充一點,當不使用 DI 時,Junit 方法需要調用私有對象的方法(可訪問),並且該對象 Quest 是在 DamselRescuingKnight 的構造函數中創建的,因此 embarkQuest() 的測試用例可以不寫。 相反,當使用 DI 時,您正在外部化對象創建,並且 Junit 方法可以創建該對象,以便它可以訪問它然后可以測試 emabarkQuest() 最終需要測試 quest 方法

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM