我正在为我的服务编写单元测试,该服务使用Entity Framework作为ORM与数据库通信。在我的服务中,有一段代码针对一个表动态投影以选择一个匿名类型对象,如下所示-
string selectQuery = "new ( " + string.Join(",", someColumns) + ")";
var values = await context.MyTable.Where(data => data.Id == someId).Select(selectQuery).ToListAsync();
之所以这样,是因为在编译时我所投影的列是未知的,并且在某些条件下可能会有所不同。使用上面的代码,生产代码可以按预期工作。
但是,当我运行测试时,它会开始抛出System.ArgumentException
并显示以下消息-
类型'System.Collections.Generic.IEnumerable'1 [DynamicClass1]'的表达式不能用于返回类型'System.Collections.Generic.IEnumerable'1 [MyTable]'
下面分享的是我的测试-
var values = new List<MyTable>
{
new MyTable
{
Id = 1,
Column1 = value1,
Column2 = value2,
Column3 = value3,
Column4 = value4
},
new MyTable
{
Id = 2,
Column1 = value5,
Column2 = value6,
Column3 = value7,
Column4 = value8
}
}.AsQueryable();
var mockValues = new Mock<DbSet<MyTable>>();
mockValues.As<IDbAsyncEnumerable<T>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(values.GetEnumerator()));
mockValues.As<IQueryable<T>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<T>(values.Provider));
mockValues.As<IQueryable<T>>().Setup(m => m.Expression).Returns(values.Expression);
mockValues.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(values.ElementType);
mockValues.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(values.GetEnumerator());
Mock<Context> contextMock = new Mock<Context>();
contextMock.Setup(x => x.MyTable).Returns(mockValues.Object);
var result = await _myService.SomeMethod(contextMock.Object, ...);
Assert.IsTrue(result);
我已向Microsoft Documentation推荐了async
支持我的测试。
通过查看异常,我猜想这应该与我在此处使用的动态投影有关,因此我将服务中的代码更改为以下代码-
var values = await context.MyTable.Where(data => data.Id == someId).Select(x => new { x.Id, x.Column1, x.Column2, x.Column4 }).ToListAsync();
基本上删除了动态结构,并且按我的预期工作了。
所以,问题是-有没有一种方法可以动态地投影列并且仍然可以进行我的测试。