EF6 - 无法模拟ObjectResult的返回值 用于单元测试
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF6 - 无法模拟ObjectResult的返回值 用于单元测试相关的知识,希望对你有一定的参考价值。
我在一个方法中有类似于此的代码,我正在尝试进行单元测试:
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实例)。它没有公共无参数构造函数,但documentation说它有一个受保护的构造函数。从我的测试来看,他的文档似乎是不正确的。
我试过的:我尝试在构造函数上创建派生类并调用base(),我也尝试使用反射(Activator.CreateInstance和使用NonPublic的BindingFlags调用ConstructorInfo,所有这些都失败了(从我的调试中可以看出,该类型确实有三个私有构造函数,所有这些构造函数都有3个或更多参数,但不幸的是,这似乎是一个很大的努力来弄清楚这些参数实际需要什么)。
我还尝试创建一个IEnumerable <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();
}
}
Here is my solution.
到目前为止,它似乎对我有用。
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>
以返回你传递给extention方法的任何内容。然后可以将模拟的ObjectResult<T>
传递给模拟的DbContext
方法设置作为方法调用的返回值。如果需要,可以将其传递给访问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());
}
以上是关于EF6 - 无法模拟ObjectResult的返回值 用于单元测试的主要内容,如果未能解决你的问题,请参考以下文章
C#,EF中执行存储过程返回一个ObjectResult对象,这个对象是延迟加载的吗?
ObjectResult 和 JsonResult 有啥区别
ObjectResult<Nullable<System.DateTime>> 没有得到存储过程结果
ASP.NET Core API 中的 IActionResult vs ObjectResult vs JsonResult