Mocking EF core dbcontext and dbset

前端 未结 4 1971
无人共我
无人共我 2020-12-28 13:29

I am using ASP.NET Core 2.2, EF Core and MOQ. When I run the test I am getting this error:

Message: System.NotSupportedException : Invalid setup on a

相关标签:
4条回答
  • 2020-12-28 14:02

    The error you're receiving is because you need to declare the Movies property on your dbcontext as Virtual.

    As someone pointed out in the comments, you should use EF's built in memory provider for testing.

    0 讨论(0)
  • 2020-12-28 14:04

    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

    0 讨论(0)
  • 2020-12-28 14:15

    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.

    0 讨论(0)
  • 2020-12-28 14:23

    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
    0 讨论(0)
提交回复
热议问题