web-dev-qa-db-fra.com

Se moquer du noyau EF dbcontext et dbset

J'utilise ASP.NET Core 2.2, EF Core et MOQ. Lorsque j'exécute le test, j'obtiens cette erreur:

Message: System.NotSupportedException: configuration non valide sur un membre non virtuel (remplaçable dans VB): x => x.Movies

Qu'est ce que je fais mal?

public class MovieRepositoryTest
{
    private readonly MovieRepository _sut;

    public MovieRepositoryTest()
    {
        var moviesMock = CreateDbSetMock(GetFakeListOfMovies());
        var mockDbContext = new Mock<MovieDbContext>();
        mockDbContext.Setup(x => x.Movies).Returns(moviesMock.Object);
        _sut = new MovieRepository(mockDbContext.Object);
    }

    [Fact]
    public void GetAll_WhenCalled_ReturnsAllItems()
    {
        //Act
        var items = _sut.GetAll();

        //Assert
        Assert.Equal(3, items.Count());
    }

    private IEnumerable<Movie> GetFakeListOfMovies()
    {
        var movies = new List<Movie>
        {
            new Movie {Id = 1, Title = "Movie 1", YearOfRelease = 2018, Genre = "Action"},
            new Movie {Id = 2, Title = "Movie 2", YearOfRelease = 2018, Genre = "Action"},
            new Movie {Id = 3, Title = "Movie 3", YearOfRelease = 2019, Genre = "Action"}
        };

        return movies;
    }

    private static Mock<DbSet<T>> CreateDbSetMock<T>(IEnumerable<T> elements) where T : class
    {
        var elementsAsQueryable = elements.AsQueryable();
        var dbSetMock = new Mock<DbSet<T>>();

        dbSetMock.As<IQueryable<T>>().Setup(m => m.Provider).Returns(elementsAsQueryable.Provider);
        dbSetMock.As<IQueryable<T>>().Setup(m => m.Expression).Returns(elementsAsQueryable.Expression);
        dbSetMock.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(elementsAsQueryable.ElementType);
        dbSetMock.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(elementsAsQueryable.GetEnumerator());

        return dbSetMock;
    }
  }

Et voici mon contexte DB, avec le Movie dbSet:

public class MovieDbContext: DbContext
{
    public MovieDbContext(DbContextOptions<MovieDbContext> options) : base(options)
    {

    }

    public DbSet<Movie> Movies { get; set; }
}

Et le référentiel avec la méthode GetAll à tester:

 public class MovieRepository: IMovieRepository
{
    private readonly MovieDbContext _moviesDbContext;
    public MovieRepository(MovieDbContext moviesDbContext)
    {
        _moviesDbContext = moviesDbContext;
    }

    public IEnumerable<Movie> GetAll()
    {
        return _moviesDbContext.Movies;
    }
}
10
MarcosF8

Je vois que vous utilisez EF core DbContext dans votre MovieRepository. Ainsi, au lieu d'utiliser la simulation, l'utilisation de la base de données EF Core InMemory sera une excellente option pour vous. Cela réduira également la complexité.

Écrivez votre méthode GetAllTest() comme suit:

[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);
        }
}

Remarque: N'oubliez pas d'installer Microsoft.EntityFrameworkCore.InMemory nuget package comme suit:

Install-Package Microsoft.EntityFrameworkCore.InMemory

Pour plus de détails: Test avec InMemory

33
TanvirArjel

Pour gagner du temps, essayez d'utiliser mon extension Moq/NSubstitute MockQueryable: https://github.com/romantitov/MockQueryable pris en charge toutes les opérations Sync/Async

//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 également pris en charge

//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);

Remarque:

  • AutoMapper pris en charge à partir de la version 1.0.4
  • DbQuery pris en charge à partir de la version 1.1.0
  • EF Core 3.0 pris en charge à partir de 3.0.0 ver
2
R.Titov

L'erreur que vous recevez est due au fait que vous devez déclarer la propriété Movies sur votre dbcontext comme virtuelle.

Comme quelqu'un l'a souligné dans les commentaires, vous devez utiliser les EF intégrés fournisseur de mémoire pour les tests.

0
Michael Brown