C# EF6模拟派生数据库集
我正在尝试将EF6的新模拟应用到我现有的代码中 我有一个扩展DbSet的类。其中一个方法调用基类(BdSet)Create方法。以下是示例代码(不是完整的解决方案或真实名称):C# EF6模拟派生数据库集,c#,unit-testing,mocking,entity-framework-6,C#,Unit Testing,Mocking,Entity Framework 6,我正在尝试将EF6的新模拟应用到我现有的代码中 我有一个扩展DbSet的类。其中一个方法调用基类(BdSet)Create方法。以下是示例代码(不是完整的解决方案或真实名称): 公共类派生数据库集:数据库集,IKeyValueDbSet,IOrderedQueryable其中tenty:class { 公共虚拟bool Add(字符串值1、字符串值2){ var entity=Create();//没有直接实现它正在调用基方法的Create方法 //用价值观做点什么 本条增加(实体); 返回tr
公共类派生数据库集:数据库集,IKeyValueDbSet,IOrderedQueryable其中tenty:class
{
公共虚拟bool Add(字符串值1、字符串值2){
var entity=Create();//没有直接实现它正在调用基方法的Create方法
//用价值观做点什么
本条增加(实体);
返回true;
}
}
我正在使用testdoubles示例进行模拟(这里是代码的和平):
var数据=新列表{
新的dummeyentity{Value1=“First”,Value2=“001”},
新的dummeyentity{Value1=“Second”,Value2=“002”}
}.AsQueryable();
var mock=new mock();
mock.CallBase=true;
mock.As().Setup(m=>m.Provider).返回(source.Provider);
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());
我已将CallBase属性设置为true,以尝试强制调用基类
但我一直收到以下错误:
System.NotImplementedException:成员“Create”尚未在继承自“DbSet1”的类型“DerivedDbSet1Proxy”上实现。“DbSet`1”的双重测试必须提供所用方法和属性的实现
我希望create调用回退到DbSet中的默认实现
有人能帮我吗?在与模拟DbSet的内部函数和异步引用进行了一些斗争之后,我提出了一个helper类,该类解决了我的大部分问题,并可能作为实现的基础。
代码如下:
public static class MockHelper
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider {
private readonly IQueryProvider _inner;
internal TestDbAsyncQueryProvider(IQueryProvider inner) { _inner = inner; }
public IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable<TEntity>(expression); }
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new TestDbAsyncEnumerable<TElement>(expression); }
public object Execute(Expression expression) { return _inner.Execute(expression); }
public TResult Execute<TResult>(Expression expression) { return _inner.Execute<TResult>(expression); }
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute(expression)); }
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute<TResult>(expression)); }
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T> {
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { }
public TestDbAsyncEnumerable(Expression expression) : base(expression) { }
public IDbAsyncEnumerator<T> GetAsyncEnumerator() { return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); }
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); }
public IQueryProvider Provider { get { return new TestDbAsyncQueryProvider<T>(this); } }
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner) { _inner = inner; }
public void Dispose() { _inner.Dispose(); }
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); }
public T Current { get { return _inner.Current; } }
object IDbAsyncEnumerator.Current { get { return Current; } }
}
public static Mock<TDbSet> CreateDbSet<TDbSet, TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TDbSet : class, IDbSet<TEntity>
where TEntity : class, new() {
var source = data.AsQueryable();
var mock = new Mock<TDbSet> { CallBase = true };
mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
if (find != null) mock.As<IDbSet<TEntity>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns(find);
return mock;
}
public static Mock<DbSet<TEntity>> CreateDbSet<TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TEntity : class, new() {
return CreateDbSet<DbSet<TEntity>, TEntity>(data, find);
}
}
公共静态类MockHelper
{
内部类TestDbAsyncQueryProvider:IDbAsyncQueryProvider{
私有只读IQueryProvider\u内部;
内部TestDbAsyncQueryProvider(IQueryProvider内部){u inner=inner;}
公共IQueryable CreateQuery(表达式){返回新的TestDbAsyncEnumerable(表达式);}
公共IQueryable CreateQuery(表达式){返回新的TestDbAsyncEnumerable(表达式);}
公共对象执行(表达式){return _inner.Execute(表达式);}
public TResult Execute(表达式){return _inner.Execute(表达式);}
公共任务ExecuteAsync(表达式表达式,CancellationToken CancellationToken){return Task.FromResult(执行(表达式));}
公共任务ExecuteAsync(表达式表达式,CancellationToken CancellationToken){return Task.FromResult(执行(表达式));}
}
内部类TestDbAsyncEnumerable:EnumerableQuery,IDbAsyncEnumerable{
公共TestDbAsyncEnumerable(IEnumerable enumerable):基(enumerable){}
公共TestDbAsyncEnumerable(表达式):基(表达式){}
public IDbAsyncEnumerator GetAsyncEnumerator(){返回新的TestDbAsyncEnumerator(this.AsEnumerable().GetEnumerator());}
IDBSyncEnumerator IDBSyncEnumerable.GetAsyncEnumerator(){return GetAsyncEnumerator();}
公共IQueryProvider提供程序{get{返回新的TestDbAsyncQueryProvider(this);}}
}
内部类TestDbAsyncEnumerator:IDbAsyncEnumerator{
私有只读IEnumerator\u内部;
公共TestDbAsyncEnumerator(IEnumerator-inner){u-inner=inner;}
public void Dispose(){u inner.Dispose();}
公共任务MoveNextAsync(CancellationToken CancellationToken){返回Task.FromResult(_inner.MoveNext());}
公共T Current{get{return}inner.Current;}
对象IDbAsyncEnumerator.Current{get{return Current;}}
}
公共静态Mock CreateDbSet(IList数据,Func find=null)
其中TDbSet:class,IDbSet
其中tenty: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(新的TestDbAsyncQueryProvider(source.Provider));
mock.As().Setup(m=>m.GetAsyncEnumerator()).Returns(新的TestDbAsyncEnumerator(data.GetEnumerator());
mock.As().Setup(m=>m.Create()).Returns(newtenty());
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())。返回(find);
返回模拟;
}
公共静态Mock CreateDbSet(IList数据,Func find=null)
其中tenty:class,new(){
返回CreateDbSet(数据,查找);
}
}
下面是一个使用示例(基于我之前给出的名称):
var数据=新列表{
新的dummeyentity{Value1=“First”,Value2=“001”},
新dummeyentity{Value1=“Second”,Value2=“002”}
};
var mockdummeyenties=MockHelper.CreateDbSet(数据,i=>data.FirstOrDefault(k=>k.Value2==(字符串)i[0]);
var mockContext=new Mock();
Setup(c=>c.dummeyenties).Returns(mockdummeyenties.Object);
任何关于如何改进此解决方案的建议都是非常受欢迎的
关于这是一个基于Andre的修改版本,对我有用。注意,我不需要异步引用。代码将添加所有派生类(如果有)
用法:
/// <summary>
///
/// </summary>
[TestMethod]
public void SomeTest()
{
//Setup
var mockContext = new Mock<FakeDbContext>();
//SomeClass can be abstract or concrete
mockContext.createFakeDBSet<SomeClass>();
var db = mockContext.Object;
//Setup create(s) if needed on concrete classes
//Mock.Get(db.Set<SomeOtherClass>()).Setup(x => x.Create()).Returns(new SomeOtherClass());
//DO Stuff
var list1 = db.Set<SomeClass>().ToList();
//SomeOtherClass derived from SomeClass
var subList1 = db.Set<SomeOtherClass>().ToList();
CollectionAssert.AreEquivalent(list1.OfType<SomeOtherClass>.ToList(), subList1);
}
//
///
///
[测试方法]
公共测试()
{
//设置
var mockContext=new Mock();
//有些类可以是抽象的,也可以是具体的
public static class MockHelper
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider {
private readonly IQueryProvider _inner;
internal TestDbAsyncQueryProvider(IQueryProvider inner) { _inner = inner; }
public IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable<TEntity>(expression); }
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new TestDbAsyncEnumerable<TElement>(expression); }
public object Execute(Expression expression) { return _inner.Execute(expression); }
public TResult Execute<TResult>(Expression expression) { return _inner.Execute<TResult>(expression); }
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute(expression)); }
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute<TResult>(expression)); }
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T> {
public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { }
public TestDbAsyncEnumerable(Expression expression) : base(expression) { }
public IDbAsyncEnumerator<T> GetAsyncEnumerator() { return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); }
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); }
public IQueryProvider Provider { get { return new TestDbAsyncQueryProvider<T>(this); } }
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner) { _inner = inner; }
public void Dispose() { _inner.Dispose(); }
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); }
public T Current { get { return _inner.Current; } }
object IDbAsyncEnumerator.Current { get { return Current; } }
}
public static Mock<TDbSet> CreateDbSet<TDbSet, TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TDbSet : class, IDbSet<TEntity>
where TEntity : class, new() {
var source = data.AsQueryable();
var mock = new Mock<TDbSet> { CallBase = true };
mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression);
mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType);
mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator());
mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider));
mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity());
mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; });
mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; });
if (find != null) mock.As<IDbSet<TEntity>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns(find);
return mock;
}
public static Mock<DbSet<TEntity>> CreateDbSet<TEntity>(IList<TEntity> data, Func<object[], TEntity> find = null)
where TEntity : class, new() {
return CreateDbSet<DbSet<TEntity>, TEntity>(data, find);
}
}
var data = new List<DummyEntity> {
new DummyEntity { Value1 = "First", Value2 = "001" } },
new DummyEntity { Value1 = "Second", Value2 = "002" } }
};
var mockDummyEntities = MockHelper.CreateDbSet<DerivedDbSet<DummyEntities>, DummyEntities>(data, i => data.FirstOrDefault(k => k.Value2 == (string)i[0]));
var mockContext = new Mock<DummyDbContext>();
mockContext.Setup(c => c.DummyEntities).Returns(mockDummyEntities.Object);
/// <summary>
///
/// </summary>
[TestMethod]
public void SomeTest()
{
//Setup
var mockContext = new Mock<FakeDbContext>();
//SomeClass can be abstract or concrete
mockContext.createFakeDBSet<SomeClass>();
var db = mockContext.Object;
//Setup create(s) if needed on concrete classes
//Mock.Get(db.Set<SomeOtherClass>()).Setup(x => x.Create()).Returns(new SomeOtherClass());
//DO Stuff
var list1 = db.Set<SomeClass>().ToList();
//SomeOtherClass derived from SomeClass
var subList1 = db.Set<SomeOtherClass>().ToList();
CollectionAssert.AreEquivalent(list1.OfType<SomeOtherClass>.ToList(), subList1);
}
/// <summary>
/// http://stackoverflow.com/questions/21943328/ef6-mocking-derived-dbsets
/// </summary>
public static class MoqSetupExtensions
{
static IEnumerable<Type> domainTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes());
public static Mock<DbSet<T>> createFakeDBSet<T>(this Mock<FakeDbContext> db, List<T> list = null, Func<List<T>, object[], T> find = null, bool createDerivedSets = true) where T : class
{
list = list ?? new List<T>();
var data = list.AsQueryable();
//var mockSet = MockHelper.CreateDbSet(list, find);
var mockSet = new Mock<DbSet<T>>() { CallBase = true };
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(() => { return data.Provider; });
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(() => { return data.Expression; });
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(() => { return data.ElementType; });
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => { return list.GetEnumerator(); });
mockSet.Setup(m => m.Add(It.IsAny<T>())).Returns<T>(i => { list.Add(i); return i; });
mockSet.Setup(m => m.AddRange(It.IsAny<IEnumerable<T>>())).Returns<IEnumerable<T>>((i) => { list.AddRange(i); return i; });
mockSet.Setup(m => m.Remove(It.IsAny<T>())).Returns<T>(i => { list.Remove(i); return i; });
if (find != null) mockSet.As<IDbSet<T>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>((i) => { return find(list, i); });
//mockSet.Setup(m => m.Create()).Returns(new T());
db.Setup(x => x.Set<T>()).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<DbSet<SubType>> createFakeDBSetSubType<BaseType, SubType>(this Mock<FakeDbContext> db, Mock<DbSet<BaseType>> baseSet)
where BaseType : class
where SubType : class, BaseType
{
var dbSet = db.Object.Set<BaseType>();
var mockSet = new Mock<DbSet<SubType>>() { CallBase = true };
mockSet.As<IQueryable<SubType>>().Setup(m => m.Provider).Returns(() => { return dbSet.OfType<SubType>().Provider; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.Expression).Returns(() => { return dbSet.OfType<SubType>().Expression; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.ElementType).Returns(() => { return dbSet.OfType<SubType>().ElementType; });
mockSet.As<IQueryable<SubType>>().Setup(m => m.GetEnumerator()).Returns(() => { return dbSet.OfType<SubType>().GetEnumerator(); });
mockSet.Setup(m => m.Add(It.IsAny<SubType>())).Returns<SubType>(i => { dbSet.Add(i); return i; });
mockSet.Setup(m => m.AddRange(It.IsAny<IEnumerable<SubType>>())).Returns<IEnumerable<SubType>>((i) => { dbSet.AddRange(i); return i; });
mockSet.Setup(m => m.Remove(It.IsAny<SubType>())).Returns<SubType>(i => { dbSet.Remove(i); return i; });
mockSet.As<IDbSet<SubType>>().Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>((i) => { return dbSet.Find(i) as SubType; });
baseSet.Setup(m => m.Create<SubType>()).Returns(() => { return mockSet.Object.Create(); });
db.Setup(x => x.Set<SubType>()).Returns(mockSet.Object);
return mockSet;
}
}