Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking EF core dbcontext and dbset

People also ask

What is the difference between DbContext and DbSet?

Intuitively, a DbContext corresponds to your database (or a collection of tables and views in your database) whereas a DbSet corresponds to a table or view in your database. So it makes perfect sense that you will get a combination of both!

What is DbSet in Entity Framework Core?

In Entity Framework Core, the DbSet represents the set of entities. In a database, a group of similar entities is called an Entity Set. The DbSet enables the user to perform various operations like add, remove, update, etc. on the entity set.

Should you use using with DbContext?

EF and EF Core DbContext types implement IDisposable . As such, best practice programming suggests that you should wrap them in a using() block (or new C# 8 using statement). Unfortunately, doing this, at least in web apps, is generally a bad idea.

What is mocking framework C#?

Mocking Frameworks (Moq, NSubstitute, Rhino Mocks, FakeItEasy, and NMock3) are used to create fake objects. We can stub, i.e., completely replace the body of member and function. It is used to isolate each dependency and help developers in performing unit testing in a concise, quick, and reliable way.


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

This is a development of R.Titovs 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.