如何模拟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
答案 0 :(得分:45)
查看AsNoTracking()
扩展方法的source code:
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
),它试图调用真实的&#39; (非模拟的)AsNoTracking()
方法,它正确地返回null。
尝试模拟AsNoTracking()
方法:
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
答案 1 :(得分:3)
你有:
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)
method实际对其论证做了些什么。另请参阅the thread What difference does .AsNoTracking() make?
如果您只是从测试类中删除涉及Setup
的{{1}}会怎样?
提供所有模拟AsNoTracking
可能会有所帮助。在这种情况下,您将发现静态方法对它们调用的成员是否可由Moq模拟(即一般意义上的虚方法/属性)。
也许你可以在必要时嘲笑非静态method DbQuery.AsNoTracking
。
答案 2 :(得分:0)
您可以使用实体框架工作量来模拟AsNoTracking(),也可以使用工作量-Official site for Effort
来模拟Db事务和实体状态。