C# 如何调用Moq实体框架SqlQuery
我已经能够使用Moq从实体框架模拟C# 如何调用Moq实体框架SqlQuery,c#,entity-framework,moq,dbcontext,C#,Entity Framework,Moq,Dbcontext,我已经能够使用Moq从实体框架模拟DbSet 但是,我现在想知道如何模拟对SqlQuery的调用。不确定这是否可能或如何实现,因为它依赖于模拟数据库上下文,知道调用了什么“查询” 下面是我试图嘲弄的 var myObjects = DbContext.Database .SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value") .ToList(); 属性和方法没有标记为virtual,因此它们(使用M
DbSet
但是,我现在想知道如何模拟对SqlQuery的调用。不确定这是否可能或如何实现,因为它依赖于模拟数据库上下文,知道调用了什么“查询”
下面是我试图嘲弄的
var myObjects = DbContext.Database
.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value")
.ToList();
属性和方法没有标记为virtual
,因此它们(使用Moq;您可以使用一个可以解释这一点的方法,但这可能比您希望的更惯性)
您需要使用某种抽象来解决这个问题,例如通过将数据库的整个查询包装在一个helper类中:
public interface IQueryHelper
{
IList<MyObject> DoYourQuery(string value);
}
public class QueryHelper : IQueryHelper
{
readonly MyDbContext myDbContext;
public QueryHelper(MyDbContext myDbContext)
{
this.myDbContext = myDbContext;
}
public IList<MyObject> DoYourQuery(string value)
{
return myDbContext.Database.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", value).ToList();
}
}
然后将IQueryHelper
注入正在测试的类的构造函数中,并对其进行模拟
您将在DoYourQuery
上丢失测试覆盖率,但现在查询是。未标记为虚拟,而是标记为虚拟
根据文件
上下文永远不会跟踪此查询的结果,即使
返回的对象类型是实体类型。使用该方法返回由
上下文
和文件
默认情况下,返回的实体由上下文跟踪;这个可以
可以通过调用返回的DbRawSqlQuery上的AsNoTracking进行更改
然后Database.SqlQuery(String,Object[])
应该与Set.SqlQuery(String,Object[]).AsNoTracking()等效(仅当T
是EF实体而不是DTO/VM时)
因此,如果您可以将实现替换为:
var myObjects = DbContext
.Set<MyObject>()
.SqlQuery("exec [dbo].[my_sproc] {0}", "some_value")
.AsNoTracking()
.ToList();
var myObjects=DbContext
.Set()
.SqlQuery(“exec[dbo].[my_存储过程]{0}”,“some_值”)
.AsNoTracking()
.ToList();
你可以模仿它如下
var list = new[]
{
new MyObject { Property = "some_value" },
new MyObject { Property = "some_value" },
new MyObject { Property = "another_value" }
};
var setMock = new Mock<DbSet<MyObject>>();
setMock.Setup(m => m.SqlQuery(It.IsAny<string>(), It.IsAny<object[]>()))
.Returns<string, object[]>((sql, param) =>
{
// Filters by property.
var filteredList = param.Length == 1
? list.Where(x => x.Property == param[0] as string)
: list;
var sqlQueryMock = new Mock<DbSqlQuery<MyObject>>();
sqlQueryMock.Setup(m => m.AsNoTracking())
.Returns(sqlQueryMock.Object);
sqlQueryMock.Setup(m => m.GetEnumerator())
.Returns(filteredList.GetEnumerator());
return sqlQueryMock.Object;
});
var contextMock = new Mock<MyDbContext>();
contextMock.Setup(m => m.Set<MyObject>()).Returns(setMock.Object);
var list=new[]
{
新的MyObject{Property=“some_value”},
新的MyObject{Property=“some_value”},
新建MyObject{Property=“另一个值”}
};
var setMock=new Mock();
setMock.Setup(m=>m.SqlQuery(It.IsAny(),It.IsAny())
.Returns((sql,param)=>
{
//按属性筛选。
变量filteredList=param.Length==1
?list.Where(x=>x.Property==param[0]作为字符串)
:列表;
var sqlQueryMock=new Mock();
Setup(m=>m.AsNoTracking())
.Returns(sqlQueryMock.Object);
Setup(m=>m.GetEnumerator())
.Returns(filteredList.GetEnumerator());
返回sqlQueryMock.Object;
});
var contextMock=new Mock();
Setup(m=>m.Set()).Returns(setMock.Object);
您可以将虚拟方法添加到数据库上下文中,以便在单元测试中重写:
public partial class MyDatabaseContext : DbContext
{
/// <summary>
/// Allows you to override queries that use the Database property
/// </summary>
public virtual List<T> SqlQueryVirtual<T>(string query)
{
return this.Database.SqlQuery<T>(query).ToList();
}
}
公共部分类MyDatabaseContext:DbContext
{
///
///允许您覆盖使用数据库属性的查询
///
公共虚拟列表SqlQueryVirtual(字符串查询)
{
返回this.Database.SqlQuery(query.ToList();
}
}
如果有人遇到这个问题。我用几种方法解决了这个问题。这只是解决这个问题的另一种方法
public interface IDatabaseContext
{
DbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
Task<int> SaveChangesAsync();
void AddOrUpdateEntity<TEntity>(params TEntity[] entities) where TEntity : class;
公共接口IDatabaseContext
{
DbSet Set(),其中T:class;
DbEntityEntry条目(T实体),其中T:类;
int SaveChanges();
任务saveChangesSync();
void AddOrUpdateEntity(参数tenty[]实体),其中tenty:class;
}public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data)
where TEntity : class, new()
{
var source = data.AsQueryable();
var mock = new Mock<DbSqlQuery<TEntity>>() {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; });
return mock;
}
publicstaticmock-CreateDbSqlQuery(IList数据)
其中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;});
返回模拟;
}
public Mock<DbSet<TestModel>> MockDbSet { get; }
....
MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>))
.Returns<string,object[]>
((sql, param) =>
{
var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models);
sqlQueryMock.Setup(x => x.AsNoTracking())
.Returns(sqlQueryMock.Object);
return sqlQueryMock.Object;
});
public Mock MockDbSet{get;}
....
MockDbSet.Setup(x=>x.SqlQuery(It.IsAny))
.返回
((sql,param)=>
{
var sqlQueryMock=MockHelper.CreateDbSqlQuery(模型);
sqlQueryMock.Setup(x=>x.AsNoTracking())
.Returns(sqlQueryMock.Object);
返回sqlQueryMock.Object;
});
这对我很有效。对我来说,这比将查询逻辑抽象为助手更好,就像上面接受的答案一样。
public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data)
where TEntity : class, new()
{
var source = data.AsQueryable();
var mock = new Mock<DbSqlQuery<TEntity>>() {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; });
return mock;
}
public Mock<DbSet<TestModel>> MockDbSet { get; }
....
MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>))
.Returns<string,object[]>
((sql, param) =>
{
var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models);
sqlQueryMock.Setup(x => x.AsNoTracking())
.Returns(sqlQueryMock.Object);
return sqlQueryMock.Object;
});