c# - unit - Marco de Entidad de Seguimiento No Simulado
unit testing entity framework (3)
¿Cómo me burlo del método AsNoTracking?
En el siguiente ejemplo, DbContext ha inyectado a la clase de servicio. Funciona bien si elimino el método de extensión AsNoTracking del método GetOrderedProducts, pero con la prueba AsNoTracking falla porque devuelve un valor nulo. También intenté burlar a AsNoTracking para devolver el valor adecuado, pero no funcionó.
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);
}
}
El problema es que AsNoTracking devuelve nulo en la prueba unitaria
Mirando el código fuente del método de extensión AsNoTracking()
:
public static IQueryable AsNoTracking(this IQueryable source)
{
var asDbQuery = source as DbQuery;
return asDbQuery != null ? asDbQuery.AsNoTracking() : CommonAsNoTracking(source);
}
Como la source
(su DbSet<Product>
que está intentando DbQuery
) es de hecho un DbQuery
(porque DbSet
se deriva de DbQuery
), intenta invocar el método AsNoTracking()
real (no AsNoTracking()
que devuelve nulo.
Trate de burlarse del método AsNoTracking()
también:
mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object);
Tienes:
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);
Pero recuerda que los métodos de extensión son solo azúcar sintáctica. Asi que:
c.Products.AsNoTracking()
es realmente justo
System.Data.Entity.DbExtensions.AsNoTracking(c.Products)
por lo tanto su configuración simulada arriba no tiene sentido.
La pregunta es qué hace el método DbExtensions.AsNoTracking(source)
estático en realidad a su argumento. También vea el hilo ¿Qué diferencia hace .AsNoTracking ()?
¿Qué sucede si simplemente elimina la Setup
de AsNoTracking
de su clase de prueba?
Podría ser útil dar a todos sus MockBehavior.Strict
. En ese caso, descubrirá si los miembros que invoca el método estático son simulables por Moq (es decir, métodos / propiedades virtuales en un sentido general). Tal vez usted puede burlarse del método no estático DbQuery.AsNoTracking
si es necesario.
puede usar Entity Framework Effort para simular el AsNoTracking (), también puede simularse de las Transacciones Db y el Estado de la Entidad usando Effort - Sitio oficial para Effort