2013-06-28 172 views
1

我有這個工廠類,我想正確地測試它。假設我有一個抽象類,它有很多孩子(繼承)。模擬靜態方法Activator.CreateInstance返回另一個類的模擬

正如你可以在我的Factory類中看到的方法BuildChild,我希望能夠在運行時創建一個子類的實例。我必須能夠在運行期間創建此實例,因爲在運行時不會知道類型。而且,我不能在這個項目中使用Unity(如果是這樣,我不會問如何實現這一點)。

下面是我想測試我的工廠類:

public class Factory 
{ 
    public AnAbstractClass BuildChild(Type childType, object parameter) 
    { 
     AnAbstractClass child = (AnAbstractClass) Activator.CreateInstance(childType); 
     child.Initialize(parameter); 
     return child; 
    } 
} 

爲了測試這一點,我想找到一種方法來模擬Activator.CreateInstance返回一個子類的我自己的嘲笑對象。我怎樣才能做到這一點?或者,如果你沒有使用Activator.CreateInstance(和Unity)有更好的方法來做到這一點,如果它更容易測試和模擬,我可以開放它!

我目前使用Moq來創建我的模擬,但由於Activator.CreateInstance是一個靜態類的靜態方法,我不知道如何做到這一點(我已經知道,Moq只能創建模擬實例對象)。我看了一下微軟的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); 
} 

Activator.CreateInstance調用類型和參數將返回,而不是創建真正的子類的新實例,我的嘲笑對象(尚未實施)。

回答

4

嗯,我很想說這不是你需要模擬的東西,因爲它應該被信任在測試中,但我猜如果這個類型來自外部源庫,那麼你可能會遇到問題。 。說,你可以實現這一目標的唯一方法是包裝Activator,使其不是一個靜態類。

事情是這樣的:

public ActivatorWrapper 
{ 
    public virtual object CreateInstance(Type type) 
    { 
     return Activator.CreateInstance(type); 
    } 
} 
+0

當你說這應該是值得信賴的,所以我不需要嘲笑它,你是完全正確的。我只是編輯我的帖子,解釋爲什麼我需要嘲笑這個類。 – Jeep87c

+0

@ Jeep87c這就是爲什麼我也給瞭解決方法。除非你想進入更強大的框架......但是那些促進糟糕的OO行爲 –

+0

哦,對不起,你可能在編輯我的文章時編輯了你的文章。沒關係,並感謝您的解決方法。由於你所說的原因,我不想使用更強大的框架。 – Jeep87c

1

你將不得不引進暴露於從類型創建實例的方法的接口。讓你的類在其構造函數中實現接口。

然後你可以嘲笑它。

然後您將擁有一個實現,該實現僅委託給您在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)); 
} 

然後,你正在測試工廠的實際功能,即使實施的變化測試不需要,而且你測試了所有的代碼。

+0

因爲測試本身的目的是測試Factory類及其方法。嘲笑工廠電話會以無用的測試結束。當我測試使用它的其他課程時,我已經嘲笑這個工廠。 – Jeep87c

+0

而且,對於你的問題「爲什麼你的測試不能檢查它是否返回方法調用中指定的類型?「因爲子類還沒有實現,根據定義,對類的單元測試決不應該依賴於其他類。 – Jeep87c

+1

但是它一直在下降,如果你引入一個包裝Activator.CreateInstance的類,你將如何去來測試這個實現嗎?我想你可以測試返回的對象是否有它的初始化方法,但是初始化方法是一種氣味,應該避免。參數應該可以通過你的抽象類聲明的構造函數傳遞看來參數不是可選的),而不是這將使這個方法「空」,因爲它只會包裝@ jeep87c我覺得你試圖解決另一個問題,在你的代碼中調用Activator.CreateInstance(參數) –

0

你可以嘗試使用如下例所示的環境環境;

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的測試類中,您都需要包含一個重置模擬對象的拆卸方法,否則存在創建測試依賴關係的危險。

相關問題