![](/img/trans.png)
[英]How can I test for ConfigureAwait(false) in a unit test for async methods?
[英]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
? 懲戒? 別的什么?
在我看來, 單元測試不應該反思強迫它的目標 。 我認為在這種測試中, 兩者都應該在同一測試中進行測試。 但這只是一個觀點。
但是,您可以進行多項測試,更改指令的順序。 嘗試添加多個,然后訪問第一個,然后是最后一個,然后是中間的一個。 每個測試都是一個場景,具有不同的順序,插入次數。 您甚至可以測試必須發生的異常狀態...例如,如果您嘗試獲取未插入的內容。
我認為單元測試的存在是為了模仿使用或強制執行規范 。 不要看是否該程序的每一位都是正確的,因為這會殺死靈活性。
那你有兩個選擇:
你可以從措辭中看出,我的選擇是#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。 這樣你就可以調用你不想測試的方法,並讓它們返回你想要的。
這里有兩個選擇。
應使用單元測試定義測試工具,並公開驗證功能是否正常工作所需的元素。 您應該在單元測試中直接使用測試工具而不是類。
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.