簡體   English   中英

如何才能正確地將相互依賴的測試方法單元化?

[英]How can I correctly unit test methods that depend on each other?

考慮以下代碼:

    private readonly Dictionary<Type, Component> _components = new Dictionary<Type, Component>();

    public Component this [Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }

如您所見, AddComponent添加到private _components變量。 但測試這種情況的唯一方法是使用索引器。 這很好,但為了測試索引器,我還必須調用AddComponent

換句話說,在索引器和AddComponent的單元測試中,每個測試都必須調用這兩個方法。 這似乎是在創造不必要的耦合。 如果索引器中存在錯誤,則我的TestAddComponent沒有理由失敗。

這里的最佳做法是什么? 我是否使用反射來獲取_components 懲戒? 別的什么?

在我看來, 單元測試不應該反思強迫它的目標 我認為在這種測試中, 兩者都應該在同一測試中進行測試。 但這只是一個觀點。

但是,您可以進行多項測試,更改指令的順序。 嘗試添加多個,然后訪問第一個,然后是最后一個,然后是中間的一個。 每個測試都是一個場景,具有不同的順序,插入次數。 您甚至可以測試必須發生的異常狀態...例如,如果您嘗試獲取未插入的內容。

我認為單元測試的存在是為了模仿使用或強制執行規范 不要看是否該程序的每一位都是正確的,因為這會殺死靈活性。

那你有兩個選擇:

  1. 使用反射或MSTest私有訪問器類在測試期間獲取和設置私有字段值。
  2. 只是不要擔心它並測試暴露的行為,即使這意味着您的測試依賴於其他地方正在測試的其他屬性或方法。

你可以從措辭中看出,我的選擇是#2 - 你應該測試暴露的行為 在您的情況下,您正在測試的暴露行為是:

  • 如果我使用AddComponent那么添加的組件應該可以通過索引器訪問
  • 如果我使用索引器,我應該能夠訪問通過AddComponent添加的任何組件

在這種情況下,相當明顯的是這些幾乎是相同的,所以我們實際上只有一個單元案例/暴露行為來測試。 是的,這個單元測試包含兩個不同的東西,但這並不重要 - 我們不是試圖測試每個方法/屬性的行為是否符合預期,而是我們想測試每個暴露的行為是否按預期工作。


作為替代方案,假設我們選擇選項1並使用私有反射來檢查_components的狀態。 在這種情況下,我們實際測試的bevahour是:

  • 如果我使用AddComponent將添加的組件添加到_components
  • 如果我使用索引器,我應該能夠訪問_components任何組件

我們現在不僅測試類的內部行為(因此,如果實現更改測試失敗,即使類按預期工作),但我們只是我們編寫的測試數量增加一倍

最重要的是,通過增加測試的復雜性,我們增加了測試本身存在錯誤的可能性 - 例如,如果我們犯了錯誤並且在測試2中,我們檢查了一些完全不同的私有字段? 在這種情況下,我們不僅為自己做了更多工作,而且我們甚至沒有測試我們想要測試的實際行為!

當您使用Microsoft Unit Test Framework時,該框架會生成一個私有訪問器類。 這應該允許您訪問您的私人類型。 有關詳細信息,請查看Microsoft的此頁面:

http://msdn.microsoft.com/en-us/library/dd293546.aspx

特別是本節:創建可以訪問internal,private和friend方法的單元測試。

我可以建議使用接口和/或虛擬方法和MOQ。 這樣你就可以調用你不想測試的方法,並讓它們返回你想要的。

這里有兩個選擇。

  1. 如前所述,一起測試功能。
  2. 修改您的類,以便將其包裝在測試工具中。

應使用單元測試定義測試工具,並公開驗證功能是否正常工作所需的元素。 您應該在單元測試中直接使用測試工具而不是類。


public class MyClass
{    
    protected readonly Dictionary<Type, Component> _components = new Dictionary<Type, Component>();

    public Component this [Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }
}

public class MyClassTestHarness : MyClass
{
    public Dictionary<Type, Component> Components
    {
        get
        {
            return _components;
        }
    }
}


忘了提到另一個選項,即使用mocking進行依賴注入。 如果您要模擬IDictionary,那么您可以驗證您的測試。


public class MyClass
{    
    protected IDictionary _components;

    public MyClass()
    {
         _components = new Dictionary();
    }

    public MyClass(IDictionary components)
    {
         _components = components;
    }

    public Component this [Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }
}

如果你想這樣做,那就離開類進入構造函數(暫時忽略IoC框架)並使用接口來引用依賴:

class ComponentManager
{
    private readonly IDictionary<Type, Component> _components;

    public ComponentManager()
        : this(new Dictionary<Type, Component>())
    { }

    public ComponentManager(IDictionary<Type, Component> components)
    {
        _components = components;
    }

    public Component this[Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }
}

現在,您可以模擬依賴關系並驗證交互。

但是,由於缺乏添加的行為,我認為真正實用的方法是直接暴露成員並丟棄聚合對象的訪問者:

class ComponentManager
{
    public Dictionary<Type, Component> Components { get; private set; }

    public ComponentManager()
    {
        Components = new Dictionary<Type, Component>();
    }
}

暫無
暫無

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

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