[英]Mock AsNoTracking Entity Framework
如何模拟AsNoTracking方法?
在下面的示例中,DbContext已注入到服务类中。如果我从GetOrderedProducts方法中删除了AsNoTracking扩展方法,它可以正常工作,但是AsNoTracking测试失败,因为它返回null。 我也尝试过模拟AsNoTracking以返回适当的值,但是没有用。
public interface IUnitOfWork
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveAllChanges();
}
public class Entites : DbContext, IUnitOfWork
{
public virtual DbSet<Product> Products { get; set; } // This is virtual because Moq needs to override the behaviour
public new virtual IDbSet<TEntity> Set<TEntity>() where TEntity : class // This is virtual because Moq needs to override the behaviour
{
return base.Set<TEntity>();
}
public int SaveAllChanges()
{
return base.SaveChanges();
}
}
public class ProductService
{
private readonly IDbSet<Product> _products;
private readonly IUnitOfWork _uow;
public ProductService(IUnitOfWork uow)
{
_uow = uow;
_products = _uow.Set<Product>();
}
public IEnumerable<Product> GetOrderedProducts()
{
return _products.AsNoTracking().OrderBy(x => x.Name).ToList();
}
}
[TestFixture]
public class ProductServiceTest
{
private readonly ProductService _productService;
public ProductServiceTest()
{
IQueryable<Product> data = GetRoadNetworks().AsQueryable();
var mockSet = new Mock<DbSet<Product>>();
mockSet.As<IQueryable<Product>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Product>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Product>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Product>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var context = new Mock<Entites>();
context.Setup(c => c.Products).Returns(mockSet.Object);
context.Setup(m => m.Set<Product>()).Returns(mockSet.Object);
context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object);
_productService = new ProductService(context.Object);
}
private IEnumerable<Product> GetRoadNetworks()
{
return new List<Product>
{
new Product
{
Id = 1,
Name = "A"
},
new Product
{
Id = 2,
Name = "B"
},
new Product
{
Id = 1,
Name = "C"
}
};
}
[Test]
public void GetOrderedProductTest()
{
IEnumerable<Product> products = _productService.GetOrderedProducts();
List<string> names = products.Select(x => x.Name).ToList();
var expected = new List<string> {"A", "B", "C"};
CollectionAssert.AreEqual(names, expected);
}
}
问题是AsNoTracking在单元测试中返回null
查看AsNoTracking()
扩展方法的源代码 :
public static IQueryable AsNoTracking(this IQueryable source)
{
var asDbQuery = source as DbQuery;
return asDbQuery != null ? asDbQuery.AsNoTracking() : CommonAsNoTracking(source);
}
由于source
(您要模拟的DbSet<Product>
)确实是DbQuery
(因为DbSet
是从DbQuery
),因此它试图调用“真实”(未经模拟)的AsNoTracking()
方法,该方法正确地返回null。
尝试模拟AsNoTracking()
方法:
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
你有:
context.Setup(c => c.Products).Returns(mockSet.Object);
context.Setup(m => m.Set<Product>()).Returns(mockSet.Object);
context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object);
但是请记住,扩展方法只是语法糖。 所以:
c.Products.AsNoTracking()
真的只是:
System.Data.Entity.DbExtensions.AsNoTracking(c.Products)
因此,您上面的模拟设置毫无意义。
问题是静态DbExtensions.AsNoTracking(source)
方法实际上对其参数做了什么。 另请参见线程.AsNoTracking()有什么区别?
如果AsNoTracking
测试类中删除涉及AsNoTracking
的Setup
,会发生什么情况?
为您提供所有MockBehavior.Strict
可能会有所帮助。 在那种情况下,您会发现静态方法对它们调用的成员是否可以被Moq模拟(即,一般意义上的虚拟方法/属性)。 如有必要,也许可以模拟非静态方法DbQuery.AsNoTracking
。
您可以使用实体框架工作量来模拟AsNoTracking(),也可以使用工作量来模拟Db事务和实体状态-工作量的官方网站
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.