EF6 Mocking派生的DbSet

我正在尝试将新的模拟EF6应用到我现有的代码中。

我有一个扩展DbSet的类。 其中一个方法调用基类(BdSet)Create方法。 这是一个示例代码(不是完整的解决方案或真实姓名):

public class DerivedDbSet : DbSet, IKeyValueDbSet, IOrderedQueryable where TEntity : class { public virtual bool Add(string value1, string value2) { var entity = Create(); // There is no direct implementation of the Create method it is calling the base method // Do something with the values this.Add(entity); return true; } } 

我正在嘲笑使用Test Doubles样本(这是代码的和平):

 var data = new List { new DummyEntity { Value1 = "First", Value2 = "001" }, new DummyEntity { Value1 = "Second", Value2 = "002" } }.AsQueryable(); var mock = new Mock<DerivedDbSet>(); mock.CallBase = true; mock.As<IQueryable>().Setup(m => m.Provider).Returns(source.Provider); mock.As<IQueryable>().Setup(m => m.Expression).Returns(source.Expression); mock.As<IQueryable>().Setup(m => m.ElementType).Returns(source.ElementType); mock.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator()); 

我已将CallBase属性设置为true以尝试强制调用基类…

但我一直收到以下错误:

System.NotImplementedException:成员’Create’尚未在1Proxy' which inherits from 'DbSet 1’的类型’DerivedDbSet 1Proxy' which inherits from 'DbSet上实现。 ‘DbSet`1’的测试双精度必须提供所使用的方法和属性的实现。

我希望create的调用回退到DbSet中的默认实现。

有人可以帮助我吗?

在使用内部函数和模拟DbSet的异步引用进行了一些讨论之后,我找到了一个帮助类来解决我的大多数问题,并可能作为某个实现的基础。 这是代码:

 public static class MockHelper { internal class TestDbAsyncQueryProvider : IDbAsyncQueryProvider { private readonly IQueryProvider _inner; internal TestDbAsyncQueryProvider(IQueryProvider inner) { _inner = inner; } public IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable(expression); } public IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable(expression); } public object Execute(Expression expression) { return _inner.Execute(expression); } public TResult Execute(Expression expression) { return _inner.Execute(expression); } public Task ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute(expression)); } public Task ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute(expression)); } } internal class TestDbAsyncEnumerable : EnumerableQuery, IDbAsyncEnumerable { public TestDbAsyncEnumerable(IEnumerable enumerable) : base(enumerable) { } public TestDbAsyncEnumerable(Expression expression) : base(expression) { } public IDbAsyncEnumerator GetAsyncEnumerator() { return new TestDbAsyncEnumerator(this.AsEnumerable().GetEnumerator()); } IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); } public IQueryProvider Provider { get { return new TestDbAsyncQueryProvider(this); } } } internal class TestDbAsyncEnumerator : IDbAsyncEnumerator { private readonly IEnumerator _inner; public TestDbAsyncEnumerator(IEnumerator inner) { _inner = inner; } public void Dispose() { _inner.Dispose(); } public Task MoveNextAsync(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); } public T Current { get { return _inner.Current; } } object IDbAsyncEnumerator.Current { get { return Current; } } } public static Mock CreateDbSet(IList data, Func find = null) where TDbSet : class, IDbSet where TEntity : class, new() { var source = data.AsQueryable(); var mock = new Mock { CallBase = true }; mock.As>().Setup(m => m.Expression).Returns(source.Expression); mock.As>().Setup(m => m.ElementType).Returns(source.ElementType); mock.As>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator()); mock.As>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider(source.Provider)); mock.As>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator(data.GetEnumerator())); mock.As>().Setup(m => m.Create()).Returns(new TEntity()); mock.As>().Setup(m => m.Add(It.IsAny())).Returns(i => { data.Add(i); return i; }); mock.As>().Setup(m => m.Remove(It.IsAny())).Returns(i => { data.Remove(i); return i; }); if (find != null) mock.As>().Setup(m => m.Find(It.IsAny())).Returns(find); return mock; } public static Mock> CreateDbSet(IList data, Func find = null) where TEntity : class, new() { return CreateDbSet, TEntity>(data, find); } } 

这是一个使用示例(基于我之前给出的名称):

 var data = new List { new DummyEntity { Value1 = "First", Value2 = "001" } }, new DummyEntity { Value1 = "Second", Value2 = "002" } } }; var mockDummyEntities = MockHelper.CreateDbSet, DummyEntities>(data, i => data.FirstOrDefault(k => k.Value2 == (string)i[0])); var mockContext = new Mock(); mockContext.Setup(c => c.DummyEntities).Returns(mockDummyEntities.Object); 

有关如何改进此解决方案的任何建议都非常受欢迎。

问候

这是基于Andre的修改版本,对我有用。 请注意,我不需要异步引用。 代码将添加所有派生类(如果有的话)

用法:

 ///  /// ///  [TestMethod] public void SomeTest() { //Setup var mockContext = new Mock(); //SomeClass can be abstract or concrete mockContext.createFakeDBSet(); var db = mockContext.Object; //Setup create(s) if needed on concrete classes //Mock.Get(db.Set()).Setup(x => x.Create()).Returns(new SomeOtherClass()); //DO Stuff var list1 = db.Set().ToList(); //SomeOtherClass derived from SomeClass var subList1 = db.Set().ToList(); CollectionAssert.AreEquivalent(list1.OfType.ToList(), subList1); } 

码:

 ///  /// http://stackoverflow.com/questions/21943328/ef6-mocking-derived-dbsets ///  public static class MoqSetupExtensions { static IEnumerable domainTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()); public static Mock> createFakeDBSet(this Mock db, List list = null, Func, object[], T> find = null, bool createDerivedSets = true) where T : class { list = list ?? new List(); var data = list.AsQueryable(); //var mockSet = MockHelper.CreateDbSet(list, find); var mockSet = new Mock>() { CallBase = true }; mockSet.As>().Setup(m => m.Provider).Returns(() => { return data.Provider; }); mockSet.As>().Setup(m => m.Expression).Returns(() => { return data.Expression; }); mockSet.As>().Setup(m => m.ElementType).Returns(() => { return data.ElementType; }); mockSet.As>().Setup(m => m.GetEnumerator()).Returns(() => { return list.GetEnumerator(); }); mockSet.Setup(m => m.Add(It.IsAny())).Returns(i => { list.Add(i); return i; }); mockSet.Setup(m => m.AddRange(It.IsAny>())).Returns>((i) => { list.AddRange(i); return i; }); mockSet.Setup(m => m.Remove(It.IsAny())).Returns(i => { list.Remove(i); return i; }); if (find != null) mockSet.As>().Setup(m => m.Find(It.IsAny())).Returns((i) => { return find(list, i); }); //mockSet.Setup(m => m.Create()).Returns(new T()); db.Setup(x => x.Set()).Returns(mockSet.Object); //Setup all derived classes if (createDerivedSets) { var type = typeof(T); var concreteTypes = domainTypes.Where(x => type.IsAssignableFrom(x) && type != x).ToList(); var method = typeof(MoqSetupExtensions).GetMethod("createFakeDBSetSubType"); foreach (var item in concreteTypes) { var invokeResult = method.MakeGenericMethod(type, item) .Invoke(null, new object[] { db, mockSet }); } } return mockSet; } public static Mock> createFakeDBSetSubType(this Mock db, Mock> baseSet) where BaseType : class where SubType : class, BaseType { var dbSet = db.Object.Set(); var mockSet = new Mock>() { CallBase = true }; mockSet.As>().Setup(m => m.Provider).Returns(() => { return dbSet.OfType().Provider; }); mockSet.As>().Setup(m => m.Expression).Returns(() => { return dbSet.OfType().Expression; }); mockSet.As>().Setup(m => m.ElementType).Returns(() => { return dbSet.OfType().ElementType; }); mockSet.As>().Setup(m => m.GetEnumerator()).Returns(() => { return dbSet.OfType().GetEnumerator(); }); mockSet.Setup(m => m.Add(It.IsAny())).Returns(i => { dbSet.Add(i); return i; }); mockSet.Setup(m => m.AddRange(It.IsAny>())).Returns>((i) => { dbSet.AddRange(i); return i; }); mockSet.Setup(m => m.Remove(It.IsAny())).Returns(i => { dbSet.Remove(i); return i; }); mockSet.As>().Setup(m => m.Find(It.IsAny())).Returns((i) => { return dbSet.Find(i) as SubType; }); baseSet.Setup(m => m.Create()).Returns(() => { return mockSet.Object.Create(); }); db.Setup(x => x.Set()).Returns(mockSet.Object); return mockSet; } } 

Microsoft发布了一个非常好的解释指南,提供了DbSet的内存实现,它完整地实现了DbSet上的所有方法。

查看http://msdn.microsoft.com/en-us/data/dn314431.aspx#doubles上的文章