when unit test mock framework context c# entity-framework-6 moq

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); ... }