![](/img/trans.png)
[英]Is it possible to mock a method with using of Activator.CreateInstance()?
[英]Mock static method Activator.CreateInstance to return a mock of another class
我有這個工廠類,我想正確地測試它。 假設我有一個抽象類,其中有許多子級(繼承)。
正如您在我的Factory類中看到的BuildChild方法一樣,我希望能夠在運行時創建一個子類的實例。 我必須能夠在運行時期間創建此實例,因為在運行時之前不會知道類型。 而且,我不能在該項目中使用Unity(如果是這樣,我不會問如何實現此目標)。
這是我要測試的Factory類:
public class Factory
{
public AnAbstractClass BuildChild(Type childType, object parameter)
{
AnAbstractClass child = (AnAbstractClass) Activator.CreateInstance(childType);
child.Initialize(parameter);
return child;
}
}
為了對此進行測試,我想找到一種方法來使Mock Activator.CreateInstance返回我自己的子類的模擬對象。 我該如何實現? 或者,也許如果您有一個更好的方法而不使用Activator.CreateInstance(和Unity),那么如果它更易於測試和模擬,我就對它開放!
我目前正在使用Moq來創建我的模擬,但是由於Activator.CreateInstance是來自靜態類的靜態方法,因此我不知道如何執行此操作(我已經知道Moq只能創建對象的模擬實例)。
我看了Microsoft的Fakes,但沒有成功(我很難理解它的工作原理並找到一些解釋清楚的示例)。
請幫我!
編輯:
我需要模擬Activator.CreateInstance,因為我想強制此方法返回另一個模擬對象。 我想要的正確方法是僅存根此方法(而不是模擬它)。
所以當我像這樣測試BuildChild時:
[TestMethod]
public void TestBuildChild()
{
var mockChildClass = new Mock(AChildClass);
// TODO: Stub/Mock Activator.CreateInstance to return mockChildClass when called with "type" and "parameter" as follow.
var type = typeof(AChildClass);
var parameter = "A parameter";
var child = this._factory.BuildChild(type, parameters);
}
使用type和parameter調用的Activator.CreateInstance將返回我的模擬對象,而不是創建實際子類(尚未實現)的新實例。
好吧,我很想說這不是您需要模擬的東西,因為應該信任它進行測試,但是我想如果該類型來自外部源庫,那么您可能會遇到問題... ,實現此目標的唯一方法是包裝Activator,使其不是靜態類。
像這樣:
public ActivatorWrapper
{
public virtual object CreateInstance(Type type)
{
return Activator.CreateInstance(type);
}
}
您將必須引入一個接口,該接口公開一個從類型創建實例的方法。 讓您的類在其構造函數中實現接口的實現。
然后,您可以嘲笑那個。
然后,您將擁有一個實現,該實現僅委托給您在Production中使用的Activator.CreateInstance。
就是說,你為什么要嘲笑這個? 為什么測試不能僅檢查它是否返回了方法調用中指定的類型?
在您進行編輯之后,為什么您不能模擬對BuildChild的工廠調用,而不是模擬工廠內部的調用。 這似乎是您要模擬的依賴項。
似乎您要么要測試工廠是否返回了正確的類型(您不需要為其模擬任何內容),要么想為工廠引入一個接口並對其進行模擬。
我認為想要模擬Activator.CreateInstance是您的代碼,告訴您設計中有些不對勁。
您可以測試工廠實現,而無需實現AnAbstractClass或無需模擬這樣的事情:
創建一個測試實現:
public class TestAnAbstractClass : AnAbstractClass
{
public object ConstructorParameter;
public TestAnAbstractClass(object constructorParameter)
{
this.constructorParameter = constructorParameter;
}
}
然后在測試中致電工廠:
[TestMethod]
public void TestBuildChild()
{
var type = typeof(TestAnAbstractClass);
var parameter = "A parameter";
var child =(TestAnAbstractClass) this._factory.BuildChild(type, parameters);
Assert.That(child.ConstructorParameter, Is.EqualTo(parameter));
}
然后,您正在測試工廠的實際功能,即使實現更改也不需要測試,然后測試所有代碼。
您可以嘗試使用下面的示例的環境上下文;
public static class SystemActivator
{
private static Dictionary<Type, object> _mockObjects;
private static Dictionary<Type, object> MockObjects
{
get { return _mockObjects; }
set
{
if (value.Any(keyValuePair => keyValuePair.Value.GetType() != keyValuePair.Key))
{
throw new InvalidCastException("object is not of the correct type");
}
_mockObjects = value;
}
}
[Conditional("DEBUG")]
public static void SetMockObjects(Dictionary<Type, object> mockObjects)
{
MockObjects = mockObjects;
}
public static void Reset()
{
MockObjects = null;
}
public static object CreateInstance(Type type)
{
if (MockObjects != null)
{
return MockObjects.ContainsKey(type) ? MockObjects[type] : Activator.CreateInstance(type);
}
return Activator.CreateInstance(type);
}
}
這是一個非常基礎的課程,可讓您了解可以使用的方法。 在測試時,您可以設置MockObjects字典,將類型與要返回的實例(即,模擬對象)配對。 然后,不要在類中使用Activator,而要使用SystemActivator進行構造。
該類的其他優點是您可以對它進行某種程度的單元測試,例如,在設置時測試返回模擬,而在不激活時返回激活器。
Activator永遠不會返回與請求的類型具有不同類型的實例,SystemActivator應該模仿此行為,這就是為什么我包含了該異常的原因。 您還可以考慮引發其他錯誤(例如,激活器無法使用此示例中顯示的CreateInstance重載創建字符串,但是如果將MockObject設置為返回字符串值,則它將-不應發生)
這種方法存在危險,永遠不要嘗試從生產代碼中設置MockObjects。 為了減少這種危險,我為方法指定了[Conditional(“ DEBUG”)]屬性。 這不會阻止開發人員嘗試在生產代碼中設置MockObjects,但是會導致發行版本中斷。
另外,在任何使用SystemActivator的測試類中,您都需要包含一個可重置模擬對象的拆卸方法,否則存在創建測試依賴項的危險。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.