簡體   English   中英

EF6 - 無法模擬ObjectResult的返回值 <T> 用於單元測試

[英]EF6 - Cannot Mock Return Value for ObjectResult<T> for Unit Test

我在一個方法中有類似於此的代碼,我正在嘗試進行單元測試:

return _context.usp_get_Some_Data(someStringParam).FirstOrDefault();

存儲的proc調用返回類型:

ObjectResult<usp_get_Some_Data_Result>. 

在我的單元測試中,我正在嘗試做這樣的事情(使用NUnit和Moq):

var procResult = new ObjectResult<usp_get_Some_Data_Result>();
mockContext.Setup(m => m.usp_get_Some_Data(It.IsAny<string>()))
    .Returns(procResult);

但是,我無法創建ObjectResult的實例(這是System.Data.Entity.Core.Objects.ObjectResult <T>,而不是舊的System.Data.Objects實例)。 它沒有公共無參數構造函數,但文檔說它有一個受保護的構造函數。 從我的測試來看,他的文檔似乎是不正確的。

我試過的:我嘗試在構造函數上創建派生類並調用base(),我也嘗試使用反射(Activator.CreateInstance和使用NonPublic的BindingFlags調用ConstructorInfo,所有這些都失敗了(從我的調試中可以看出,該類型確實有三個私有構造函數,所有這些構造函數都有3個或更多參數,但不幸的是,這似乎是一個很大的努力來弄清楚這些參數實際需要什么)。

我還嘗試創建一個IEnumberable <usp_get_Some_Data_Result>並將其轉換為ObjectResult <usp_get_Some_Data_Result>但是轉換失敗了。 另外,我嘗試過類似的東西

var mockObjectResult = new Mock<ObjectResult<usp_get_Some_Data_Result>>();

幾乎所有我試過的都失敗了,關於默認構造函數不可用的類似錯誤。

問題 :有沒有辦法為單元測試創​​建一個ObjectResult <T>實例,或者是否可以創建任何可以成功轉換為ObjectResult <T>的其他類型?

也許我錯過了什么,但你不能這樣做:

class TestableObjectResult<T> : ObjectResult<T>
{
}

然后在你的測試中:

var mockObjectResult = new Mock<TestableObjectResult<usp_get_Some_Data_Result>>();

MockObject確實有一個受保護的構造函數,你沒有必要做任何事情來調用它,因為它沒有任何參數,當你構造可測試版本時,自動連線會處理它,所以我不是確定你的意思是“在構造函數上調用base()”......

如果我右鍵單擊ObjectResult並選擇goto定義,文件的頂部如下所示:

public class ObjectResult<T> : ObjectResult, IEnumerable<T>, IEnumerable, IDbAsyncEnumerable<T>, IDbAsyncEnumerable
{
    // Summary:
    //     This constructor is intended only for use when creating test doubles that
    //     will override members with mocked or faked behavior. Use of this constructor
    //     for other purposes may result in unexpected behavior including but not limited
    //     to throwing System.NullReferenceException.
    protected ObjectResult();

如上所述,我正在添加此答案以涵蓋創建枚舉器,以便上面可以實際測試一些假數據:

在[TestFixture]類中,創建如下方法:

private static IEnumerator<usp_get_Some_Data_Result> GetSomeDataResultEnumerator()
{
    yield return FakeSomeDataResult.Create(1, true);
    yield return FakeSomeDataResult.Create(2, false);
}

正如前面的答案所提供的,這個方便的小包裝類允許實例化ObjectResult:

public class TestableObjectResult<T> : ObjectResult<T> { }

如前一個答案中所提供的,在[SetUp]方法中:

var mockObjectResult = new Mock<TestableObjectResult<usp_get_Some_Data_Result>>();

在創建新的Mock之后,將其設置為返回Enumerator:

mockObjectResult.Setup(d => d.GetEnumerator()).Returns(GetSomeDataResultEnumerator());

現在,OP可以從mockContext返回一些偽數據,而不會在嘗試獲取枚舉數時拋出空引用異常:

mockContext.Setup(m => m.usp_get_Some_Data(It.IsAny<string>()))
    .Returns(mockObjectResult);

順便說一下,我只是使用幫助程序類來構造我的假數據,以消除冗余:

public static class FakeSomeDataResult
{
    public static usp_get_Some_Data_Result Create(int index)
    {
        return new usp_get_Some_Data_Result
        {
            SomeFriendlyNameProperty = string.Format("Some Data Result {0}", index),
        };
    } 
}

正如@forsvarir所提到的,您可以創建一個TestableObjectResult類並重寫GetEnumerator()以返回您想要的任何內容。

像這樣的東西:

private class TestableObjectResult : ObjectResult<Animal>
    {
        public override IEnumerator<Animal> GetEnumerator()
        {
            return new List<Animal>() { new Animal(), new Animal() }.GetEnumerator();
        }
    }

這是我的解決方案。

到目前為止,它似乎對我有用。

public static class MoqExtentions
{
    public static void SetupReturn<T>(this Mock<ObjectResult<T>> mock, T whatToReturn)
    {
        IEnumerator<T> enumerator = ((IEnumerable<T>) new T[] {whatToReturn}).GetEnumerator();

        mock.Setup(or => or.GetEnumerator())
            .Returns(() => enumerator);
    }
}

現在,你可以模擬一個ObjectResult<T>並調用設置的進一步擴展方法ObjectResult<T>返回無論你傳遞到進一步擴展方法。 然后, DbContext ObjectResult<T>可以作為方法調用的返回值傳遞給DbContext方法設置。 如果需要,可以將其傳遞給訪問ObjectResult<T>ObjectResult<T> repo方法。

以下是使用模擬的ObjectResult<string>的測試示例

    public void MockObjectResultReturn_OfString_Test()
    {
        // arrange
        const string shouldBe = "Hello World!";
        var sut = new Mock<ObjectResult<string>>();

        // act
        sut.SetupReturn<string>(shouldBe);

        //assert
        Assert.IsNotNull(sut);
        Assert.IsNotNull(sut.Object);
        Assert.AreEqual(shouldBe, sut.Object?.FirstOrDefault());
    }

暫無
暫無

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

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