首先,ObjectResult没有公共的无参构造函数,因此需要先创建一个可测试的ObjectResult包装器。这篇文章中@forsvarir (
https://stackoverflow.com/users/592182/forsvarir) 的答案让我正确地思考了这些问题(
EF6 - 无法为单元测试模拟ObjectResult<T>的返回值):
using System.Data.Entity.Core.Objects;
namespace MyNamespace.Mocks
{
public class TestableEfObjectResult<T> : ObjectResult<T> { }
}
当然,需要模拟 DbContext。然后设置您的方法以返回适当的模拟枚举器。为了方便起见,我创建了一个方法来帮助创建 Mock EF Results,以避免我的测试代码变得混乱和冗余。这可以存在于您为测试拥有的某些实用类中,尽管我在此处仅将其包含为私有方法。关键是,当调用 GetEnumerator 时,模拟对象结果需要返回枚举器:
namespace MyNamespace.Mocks
{
public class MockSomeDbEntities
{
public static Mock<SomeDbEntities> Default
{
get
{
var mockSomeDbEntities = new Mock<SomeDbEntities>();
mockSomeDbEntities
.Setup(e => e.SomeMethod(It.IsAny<int>()))
.Returns(MockEfResult(Enumerators.SomeCollection).Object);
return mockSomeDbEntities;
}
}
private static Mock<TestableEfObjectResult<T>> MockEfResult<T>(Func<IEnumerator<T>> enumerator) where T : class
{
var mock = new Mock<TestableEfObjectResult<T>>();
mock.Setup(m => m.GetEnumerator()).Returns(enumerator);
return mock;
}
}
}
我创建的 Enumerators 类用于在调用模拟函数时回传枚举器,其代码如下。在此示例中,我让仿造的枚举器创建了 5 行数据:
using System;
using System.Collections.Generic;
namespace MyNamespace.FakeData
{
public static class Enumerators
{
public static IEnumerator<Some_Result> SomeCollection()
{
yield return FakeSomeResult.Create(1);
yield return FakeSomeResult.Create(2);
yield return FakeSomeResult.Create(3);
yield return FakeSomeResult.Create(4);
yield return FakeSomeResult.Create(5);
}
}
}
正如您所看到的,这只是依靠一个创建每个虚假数据行的类:
namespace MyNamespace.FakeData
{
public static class FakeSomeResult
{
public static Some_Result Create(int id)
{
return new Some_Result
{
Id = id,
};
}
}
}
能够在这个层次上进行模拟真正提高了我进行BDD的能力,只需要模拟或伪造外围部分,而不需要模拟或伪造
我的代码,因此我可以获得更完整的测试覆盖率。
希望这能帮助那些像我一样正在寻找一种干净简洁的方式来模拟Entity Framework 6的人。