I am following the instructions here to try to mock my DbSet
and DbContext
for unit testing using Moq.
The service that I'm testing looks like this
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.MyItems.AddOrUpdate(item);
this.context.SaveChanges();
}
}
My unit test looks like this
[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"
});
}
When I run the test, I am getting the exception
System.InvalidOperationException: Unable to call public, instance method AddOrUpdate on derived IDbSet<T> type 'Castle.Proxies.DbSet``1Proxy'. Method not found.
I assume the problem is because AddOrUpdate
is an extension method on DbSet
. I do have System.Data.Entity.Migrations
included in my test .cs file.
I tried adding the line
myItems.Setup(e => e.AddOrUpdate(It.IsAny<MyItem>()));
to my unit test, but then I get the exception
System.NotSupportedException: Expression references a method that does not belong to the mocked object: e => e.AddOrUpdate(new[] { It.IsAny() })
Is there any way that I can have my unit test work when my method being tested is using AddOrUpdate
?
I came across this old question after searching for similar issue.
All we need to do is to create MockableDbSetWithExtensions abstract class, and use it instead of DbSet, whenever you want to test a method which calls AddOrUpdate.
It is how Entity Framework team tests their code too.
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);
}
[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);
...
}
As Nkosi said in the comments you add wrapper around the AddOrUpdate. You can implement the wrapper like this:
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)
}
}
}
And then call it from your class like:
public class MyItemService
{
private MyContext context;
public void AddItem(MyItem item)
{
this.context.AddOrUpdate(item);
this.context.SaveChanges();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With