I am developing an Asp.Net mvc application. I am doing unit testing to my application. I use Moq to mock objects. In my test I need to mock database context and its DbSets. I can mock database context and test. But the problem is I have to change from DbSet<Entity>
Entities to IDbSet<Entity>
Entities in context class.
This is example of how I mock context class
[TestMethod]
public void GenerateItemCode_IncreaseDigit()
{
var data = new List<Item>{
new Item{
Id = 2,
ItemCode = "CD345678"
}
}.AsQueryable();
var dbSetMock = new Mock<IDbSet<Item>>();
dbSetMock.Setup(m => m.Provider).Returns(data.Provider);
dbSetMock.Setup(m => m.Expression).Returns(data.Expression);
dbSetMock.Setup(m => m.ElementType).Returns(data.ElementType);
dbSetMock.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var storeContext = new Mock<StoreContext>();
storeContext.Setup(x => x.Items).Returns(dbSetMock.Object);
ItemRepo itemRepo = new ItemRepo(storeContext.Object);
string itemCode = itemRepo.GenerateItemCode();
Assert.AreEqual(itemCode, "CD345679");
}
This is context class
public class StoreContext : DbContext, IDisposable
{
public StoreContext():base("DefaultConnection")
{
}
public virtual IDbSet<Item> Items { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
When I use IDbSet instead of DbSet, my application goes down in performance. Because I cannot use as below
context.Items.Include("Promotions")//Cannot use Include with IDbSet
context.Items.AddRange(items)//cannot use AddRange with IDbSet
context.Items.RemoveRange(items)//cannot with IDbSet
So I changed from IDbSet to DbSet in context class as below
public virtual DbSet<Item> Items { get; set; }
Then unit tests start throwing errors. Because items of context class cannot be mocked with the way I am mocking. Below is the screenshot of errors.
So I changed from IDbSet to DbSet in Unit Test. Then the errors become like this.
How can I mock DbSet entities of context class for unit tests?
After the question update I can reproduce your problem. You have just forget to cast to the correct interface IQueryable:
var dbSetMock = new Mock<DbSet<Item>>();
dbSetMock.As<IQueryable<Item>>().Setup(m => m.Provider).Returns(data.Provider);
dbSetMock.As<IQueryable<Item>>().Setup(m => m.Expression).Returns(data.Expression);
dbSetMock.As<IQueryable<Item>>().Setup(m => m.ElementType).Returns(data.ElementType);
dbSetMock.As<IQueryable<Item>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
If you want to know why you must cast to make it possible to setting the provider that because the explicit interface implementation of the IQueryable. More info : https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx