C# EF6-无法模拟ObjectResult的返回值<;T>;用于单元测试

C# EF6-无法模拟ObjectResult的返回值<;T>;用于单元测试,c#,unit-testing,mocking,entity-framework-6,moq,C#,Unit Testing,Mocking,Entity Framework 6,Moq,在我尝试进行单元测试的方法中,我有类似的代码: return _context.usp_get_Some_Data(someStringParam).FirstOrDefault(); 存储的过程调用返回类型: ObjectResult<usp_get_Some_Data_Result>. ObjectResult。 在我的单元测试中,我正在尝试这样做(使用NUnit和Moq): var procResult=new ObjectResult(); mockContext.S

在我尝试进行单元测试的方法中,我有类似的代码:

return _context.usp_get_Some_Data(someStringParam).FirstOrDefault();
存储的过程调用返回类型:

ObjectResult<usp_get_Some_Data_Result>. 
ObjectResult。
在我的单元测试中,我正在尝试这样做(使用NUnit和Moq):

var procResult=new ObjectResult();
mockContext.Setup(m=>m.usp\u获取一些数据(It.IsAny())
.返回(proclesult);
但是,我无法创建ObjectResult的实例(这是System.Data.Entity.Core.Objects.ObjectResult,而不是较旧的System.Data.Objects实例)。它没有公共的无参数构造函数,但声明它有一个受保护的构造函数。根据我的测试,他的文档似乎不正确

我所尝试的: 我尝试过在构造函数上创建一个派生类并调用base(),还尝试过使用反射(Activator.CreateInstance和使用BindingFlags为NonPublic调用ConstructorInfo),所有这些都失败了(从我的调试中可以看出,该类型确实有三个私有构造函数,所有这些构造函数都有3个或更多参数,但不幸的是,要弄清楚这些参数实际需要什么似乎是一项重大工作)

我还尝试创建IEnumberable并将其强制转换为ObjectResult,但转换失败

var mockObjectResult = new Mock<ObjectResult<usp_get_Some_Data_Result>>();
var mockObjectResult=new Mock();
我尝试过的几乎所有方法都失败了,出现了一个类似的错误,即默认构造函数不可用

问题
是否有任何方法可以为单元测试创建ObjectResult实例,或者是否有任何其他类型可以成功转换为ObjectResult?

也许我遗漏了一些东西,但您不能这样做:

class TestableObjectResult<T> : ObjectResult<T>
{
}
类TestableObjectResult:ObjectResult
{
}
然后在你的测试中:

var mockObjectResult = new Mock<TestableObjectResult<usp_get_Some_Data_Result>>();
var mockObjectResult=new Mock();
MockObject确实有一个受保护的构造函数,您实际上不必做任何事情来调用它,因为它没有任何参数,当您构造可测试版本时,自动连接将处理它,所以我不确定“在构造函数上调用base()”是什么意思

如果我右键单击ObjectResult并选择goto definition,则文件顶部如下所示:

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();
private class TestableObjectResult : ObjectResult<Animal>
    {
        public override IEnumerator<Animal> GetEnumerator()
        {
            return new List<Animal>() { new Animal(), new Animal() }.GetEnumerator();
        }
    }
公共类ObjectResult:ObjectResult,IEnumerable,IEnumerable,IDbAsyncEnumerable,IDbAsyncEnumerable
{
//总结:
//此构造函数仅用于创建测试双精度
//将重写具有模拟或伪造行为的成员。使用此构造函数
//出于其他目的,可能导致意外行为,包括但不限于
//无法引发System.NullReferenceException。
受保护的ObjectResult();

如前所述,我添加此答案是为了涵盖创建枚举器,以便上面的内容可以实际测试一些虚假数据:

在[TestFixture]类中,创建如下方法:

private static IEnumerator<usp_get_Some_Data_Result> GetSomeDataResultEnumerator()
{
    yield return FakeSomeDataResult.Create(1, true);
    yield return FakeSomeDataResult.Create(2, false);
}
现在,OP可以从mockContext返回一些伪数据,而不会在尝试获取枚举数时引发null引用异常:

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

正如@forsvarir所提到的,您可以创建一个TestableObjectResult类并重写GetEnumerator()以返回您想要的任何内容

大概是这样的:

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();
private class TestableObjectResult : ObjectResult<Animal>
    {
        public override IEnumerator<Animal> GetEnumerator()
        {
            return new List<Animal>() { new Animal(), new Animal() }.GetEnumerator();
        }
    }
私有类TestableObjectResult:ObjectResult
{
公共重写IEnumerator GetEnumerator()
{
返回新列表(){new Animal(),new Animal()}.GetEnumerator();
}
}
这是我的解决方案。 到目前为止,它似乎对我有效


从ObjectResult继承的上述类声明未能编译:“错误1类型'System.Data.Entity.Core.Objects.ObjectResult'未定义构造函数”如果我将构造函数添加到TestableObjectResult类中,我也会遇到同样的错误。@dgavian您使用的是哪个版本的EF?我刚刚从nuget重新获取了它,它编译得很好。@dgavian我已将源代码的顶部添加到我的答案中,以演示它应该在那里。。右键单击它并转到定义。如果您不同,那么你可能没有使用你认为你是的版本…这很奇怪。我不认为从6.1.0更新到6.1.3会有那么大的不同,但它现在可以编译了。谢谢!@crazyTech如上面的问题/评论中所述,这是指实体框架的ObjectResult,版本6。
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),
        };
    } 
}
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);
    }
}
    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());
    }