簡體   English   中英

模擬靜態方法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.

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