模拟AsNoTrackingentity framework
我如何模拟AsNoTracking方法?
在下面的示例中,DbContext已注入服务类。如果我从GetOrderedProducts方法中删除AsNoTracking扩展方法,但是AsNoTracking测试失败,因为它返回null,它工作正常。 我也尝试过模拟AsNoTracking来返回正确的值,但它没有用。
public interface IUnitOfWork { IDbSet Set() where TEntity : class; int SaveAllChanges(); } public class Entites : DbContext, IUnitOfWork { public virtual DbSet Products { get; set; } // This is virtual because Moq needs to override the behaviour public new virtual IDbSet Set() where TEntity : class // This is virtual because Moq needs to override the behaviour { return base.Set(); } public int SaveAllChanges() { return base.SaveChanges(); } } public class ProductService { private readonly IDbSet _products; private readonly IUnitOfWork _uow; public ProductService(IUnitOfWork uow) { _uow = uow; _products = _uow.Set(); } public IEnumerable GetOrderedProducts() { return _products.AsNoTracking().OrderBy(x => x.Name).ToList(); } } [TestFixture] public class ProductServiceTest { private readonly ProductService _productService; public ProductServiceTest() { IQueryable data = GetRoadNetworks().AsQueryable(); var mockSet = new Mock<DbSet>(); mockSet.As<IQueryable>().Setup(m => m.Provider).Returns(data.Provider); mockSet.As<IQueryable>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As<IQueryable>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); var context = new Mock(); context.Setup(c => c.Products).Returns(mockSet.Object); context.Setup(m => m.Set()).Returns(mockSet.Object); context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object); _productService = new ProductService(context.Object); } private IEnumerable GetRoadNetworks() { return new List { new Product { Id = 1, Name = "A" }, new Product { Id = 2, Name = "B" }, new Product { Id = 1, Name = "C" } }; } [Test] public void GetOrderedProductTest() { IEnumerable products = _productService.GetOrderedProducts(); List names = products.Select(x => x.Name).ToList(); var expected = new List {"A", "B", "C"}; CollectionAssert.AreEqual(names, expected); } }
问题是AsNoTracking在unit testing中返回null
查看AsNoTracking()
扩展方法的源代码 :
public static IQueryable AsNoTracking(this IQueryable source) { var asDbQuery = source as DbQuery; return asDbQuery != null ? asDbQuery.AsNoTracking() : CommonAsNoTracking(source); }
由于source
(您尝试模拟的DbSet
)确实是DbQuery
(因为DbSet
派生自DbQuery
),它会尝试调用正确返回null的“真实”(非模拟) AsNoTracking()
方法。
尝试模拟AsNoTracking()
方法:
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
你有:
context.Setup(c => c.Products).Returns(mockSet.Object); context.Setup(m => m.Set()).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
的Setup
会发生什么?
给你所有的MockBehavior.Strict
可能会有所帮助。 在这种情况下,您将发现静态方法调用它们的成员是否可由Moq模拟(即一般意义上的虚方法/属性)。 也许你可以在必要时模拟非静态方法DbQuery.AsNoTracking
。