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’的测试双精度必须提供所使用的方法和属性的实现。
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
这是一个使用示例(基于我之前给出的名称):
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);
/// /// /// [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; } }