Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking EF DbContext with Moq

I'm trying to create a unit test for my service with a mocked DbContext. I created an interface IDbContext with the following functions:

public interface IDbContext : IDisposable {     IDbSet<T> Set<T>() where T : class;     DbEntityEntry<T> Entry<T>(T entity) where T : class;     int SaveChanges(); } 

My real context implements this interface IDbContext and DbContext.

Now I'm trying to mock the IDbSet<T> in the context, so it returns a List<User> instead.

[TestMethod] public void TestGetAllUsers() {     // Arrange     var mock = new Mock<IDbContext>();     mock.Setup(x => x.Set<User>())         .Returns(new List<User>         {             new User { ID = 1 }         });      UserService userService = new UserService(mock.Object);      // Act     var allUsers = userService.GetAllUsers();      // Assert     Assert.AreEqual(1, allUsers.Count()); } 

I always get this error on .Returns:

The best overloaded method match for 'Moq.Language.IReturns<AuthAPI.Repositories.IDbContext,System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>.Returns(System.Func<System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>)' has some invalid arguments 
like image 596
Gaui Avatar asked Sep 21 '14 14:09

Gaui


People also ask

Can you mock a class with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces. However, there are a few limitations you should be aware of. The classes to be mocked can't be static or sealed, and the method being mocked should be marked as virtual.

What can be mocked with Moq?

Unit testing is a powerful way to ensure that your code works as intended. It's a great way to combat the common “works on my machine” problem. Using Moq, you can mock out dependencies and make sure that you are testing the code in isolation.

How do I use Moq FromSqlInterpolated?

You can't mock FromSqlInterpolated directly as it's extension method. Under the covers it invokes CreateQuery<T> on the DbSet<T> queryable provider. If you don't want to set up integration tests, the library EntityFrameworkCore. Testing, which I am the author of, can do it.

What is Moq mocking framework?

Moq is a mocking framework built to facilitate the testing of components with dependencies. As shown earlier, dealing with dependencies could be cumbersome because it requires the creation of test doubles like fakes. Moq makes the creation of fakes redundant by using dynamically generated types.


2 Answers

I managed to solve it by creating a FakeDbSet<T> class that implements IDbSet<T>

public class FakeDbSet<T> : IDbSet<T> where T : class {     ObservableCollection<T> _data;     IQueryable _query;      public FakeDbSet()     {         _data = new ObservableCollection<T>();         _query = _data.AsQueryable();     }      public virtual T Find(params object[] keyValues)     {         throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");     }      public T Add(T item)     {         _data.Add(item);         return item;     }      public T Remove(T item)     {         _data.Remove(item);         return item;     }      public T Attach(T item)     {         _data.Add(item);         return item;     }      public T Detach(T item)     {         _data.Remove(item);         return item;     }      public T Create()     {         return Activator.CreateInstance<T>();     }      public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T     {         return Activator.CreateInstance<TDerivedEntity>();     }      public ObservableCollection<T> Local     {         get { return _data; }     }      Type IQueryable.ElementType     {         get { return _query.ElementType; }     }      System.Linq.Expressions.Expression IQueryable.Expression     {         get { return _query.Expression; }     }      IQueryProvider IQueryable.Provider     {         get { return _query.Provider; }     }      System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()     {         return _data.GetEnumerator();     }      IEnumerator<T> IEnumerable<T>.GetEnumerator()     {         return _data.GetEnumerator();     } } 

Now my test looks like this:

[TestMethod] public void TestGetAllUsers() {     //Arrange     var mock = new Mock<IDbContext>();     mock.Setup(x => x.Set<User>())         .Returns(new FakeDbSet<User>         {             new User { ID = 1 }         });      UserService userService = new UserService(mock.Object);      // Act     var allUsers = userService.GetAllUsers();      // Assert     Assert.AreEqual(1, allUsers.Count()); } 
like image 63
Gaui Avatar answered Sep 28 '22 08:09

Gaui


In case anyone is still interested, I was having the same problem and found this article very helpful: Entity Framework Testing with a Mocking Framework (EF6 onwards)

It only applies to Entity Framework 6 or newer, but it covers everything from simple SaveChanges tests to async query testing all using Moq (and a few of manual classes).

like image 22
eitamal Avatar answered Sep 28 '22 08:09

eitamal