Mocking EF core dbcontext and dbset
I see you are using EF core DbContext
in your MovieRepository
. So instead of using mock, Using EF Core InMemory
database will be a great option for you. This will also reduce the complexity.
Write your GetAllTest()
method as follows:
[Fact]
public void GetAllTest()
{
var options = new DbContextOptionsBuilder<MovieDbContext>()
.UseInMemoryDatabase(databaseName: "MovieListDatabase")
.Options;
// Insert seed data into the database using one instance of the context
using (var context = new MovieDbContext(options))
{
context.Movies.Add(new Movie {Id = 1, Title = "Movie 1", YearOfRelease = 2018, Genre = "Action"});
context.Movies.Add(new Movie {Id = 2, Title = "Movie 2", YearOfRelease = 2018, Genre = "Action"});
context.Movies.Add(nnew Movie {Id = 3, Title = "Movie 3", YearOfRelease = 2019, Genre = "Action"});
context.SaveChanges();
}
// Use a clean instance of the context to run the test
using (var context = new MovieDbContext(options))
{
MovieRepository movieRepository = new MovieRepository(context);
List<Movies> movies == movieRepository.GetAll()
Assert.Equal(3, movies.Count);
}
}
Note: Don't forget to install Microsoft.EntityFrameworkCore.InMemory
nuget package as follows:
Install-Package Microsoft.EntityFrameworkCore.InMemory
For more details: Testing with InMemory
To save your time, try to use my Moq/NSubstitute extension MockQueryable: https://github.com/romantitov/MockQueryable supported all Sync/Async operations
//1 - create a List<T> with test items
var users = new List<UserEntity>()
{
new UserEntity,
...
};
//2 - build mock by extension
var mock = users.AsQueryable().BuildMock();
//3 - setup the mock as Queryable for Moq
_userRepository.Setup(x => x.GetQueryable()).Returns(mock.Object);
//3 - setup the mock as Queryable for NSubstitute
_userRepository.GetQueryable().Returns(mock);
DbSet also supported
//2 - build mock by extension
var mock = users.AsQueryable().BuildMockDbSet();
//3 - setup DbSet for Moq
var userRepository = new TestDbSetRepository(mock.Object);
//3 - setup DbSet for NSubstitute
var userRepository = new TestDbSetRepository(mock);
Note:
- AutoMapper supported from 1.0.4 ver
- DbQuery supported from 1.1.0 ver
- EF Core 3.0 supported from 3.0.0 ver
Use the Moq.EntityFrameworkCore package.
It is as easy as:
var myDbContextMock = new Mock<MyDbContext>();
IList<Entity> entities = new List<Entity>() { new Entity(), new Entity() };
myDbContextMock.Setup(x => x.Entities).ReturnsDbSet(entities);
This is a development of R.Titov
s answer done in ASP.NET Core 3.1:
Constructing the Moq (generic method)
The data is cloned to allow for tests to run in parallel and prevent a test to access data changed by another.
public static Mock<DbSet<TEnt>> SetDbSetData<TEnt>(this Mock<IApplicationDbContext> dbMock,
IList<TEnt> list, bool clone = true)
where TEnt : class
{
var clonedList = clone ? list.DeepClone().ToList() : list.ToList();
var mockDbSet = clonedList.AsQueryable().BuildMockDbSet();
dbMock.Setup(m => m.Set<TEnt>()).Returns(mockDbSet.Object);
dbMock.Setup(m => m.ReadSet<TEnt>()).Returns(mockDbSet.Object.AsQueryable());
return mockDbSet;
}
Using some test data
_appUserDbSetMock = _dbMock.SetDbSetData(ApplicationUserTestData.ApplicationUserData);
Example test
[Fact]
private async Task Handle_ShouldAddANewUser()
{
var command = new CreateApplicationUserCommand
{
// ...
};
await _handler.Handle(command, default);
_appUserDbSetMock.Verify(m => m.AddAsync(It.IsAny<ApplicationUser>(), default), Times.Once);
}
One advantage of using MoqQueryable is that there is no need for a generic repository since DbSet acts like one and the mocking is very easy.