c# - unit - Mocking DbSet.AddOrUpdate
mocking entity framework when unit testing (2)
Estoy siguiendo las instrucciones aquí para tratar de burlar mi DbSet
y DbContext
para probar la unidad usando Moq.
El servicio que estoy probando se parece a esto
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.MyItems.AddOrUpdate(item);
this.context.SaveChanges();
}
}
Mi prueba de unidad se ve así
[TestMethod]
public void AddItem_ShouldSucceed()
{
var myItems = new Mock<DbSet<MyItem>>();
var context = new Mock<MyContext>();
context.Setup(e => e.MyItems).Returns(myItems.Object);
MyItemService service = new MyItemService(context.Object);
service.AddItem(new MyItem
{
Id = "1234"
});
}
Cuando realizo la prueba, obtengo la excepción
System.InvalidOperationException: Unable to call public, instance method AddOrUpdate on derived IDbSet<T> type ''Castle.Proxies.DbSet``1Proxy''. Method not found.
Supongo que el problema es porque AddOrUpdate
es un método de extensión en DbSet
. Tengo System.Data.Entity.Migrations
incluido en mi archivo .cs de prueba.
Intenté agregar la línea
myItems.Setup(e => e.AddOrUpdate(It.IsAny<MyItem>()));
a mi prueba de unidad, pero luego recibo la excepción
System.NotSupportedException: Expression hace referencia a un método que no pertenece al objeto burlado: e => e.AddOrUpdate (new [] {It.IsAny ()})
¿Hay alguna forma en que pueda hacer funcionar mi prueba de unidad cuando mi método está siendo probado usando AddOrUpdate
?
Como dijo Nkosi en los comentarios, agregas un contenedor alrededor de AddOrUpdate. Puede implementar el contenedor de esta manera:
public class MyDbContext: DbContext
{
public virtual DbSet<MyItem> Items {get; set;}
public virtual AddOrUpdate<T>(T item)
{
if(typeof(T) == typeof(MyItem))
{
Items.AddOrUpdate(item as MyItem)
}
}
}
Y luego llámalo desde tu clase como:
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.AddOrUpdate(item);
this.context.SaveChanges();
}
}
Me encontré con esta vieja pregunta después de buscar un problema similar.
Todo lo que tenemos que hacer es crear la clase abstracta MockableDbSetWithExtensions y usarla en lugar de DbSet , siempre que desee probar un método que llame a AddOrUpdate .
Así es como el equipo de Entity Framework también prueba su código.
public abstract class MockableDbSetWithExtensions<T> : DbSet<T>
where T : class
{
public abstract void AddOrUpdate(params T[] entities);
public abstract void AddOrUpdate(Expression<Func<T, object>>
identifierExpression, params T[] entities);
}
Uso
[TestMethod]
public void AddItem_ShouldSucceed()
{
var myItems = new Mock<MockableDbSetWithExtensions<MyItem>>();
var context = new Mock<MyContext>();
context.Setup(e => e.MyItems).Returns(myItems.Object);
MyItemService service = new MyItemService(context.Object);
...
}