webentwicklung-frage-antwort-db.com.de

Verspotten des EF-Kerns dbcontext und dbset

Ich verwende ASP.NET Core 2.2, EF Core und MOQ. Wenn ich den Test durchführe, wird folgende Fehlermeldung angezeigt:

Nachricht: System.NotSupportedException: Ungültiges Setup für ein nicht virtuelles (in VB überschreibbares) Mitglied: x => x.Movies

Was mache ich falsch?

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

Und das ist mein DB-Kontext mit dem Movie dbSet:

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

    }

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

Und das Repository mit der zu testenden Methode GetAll:

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

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

Ich sehe, dass Sie EF Core DbContext in Ihrem MovieRepository verwenden. Anstatt Schein zu verwenden, ist die Verwendung der EF Core InMemory -Datenbank eine großartige Option für Sie. Dies verringert auch die Komplexität.

Schreiben Sie Ihre GetAllTest() Methode wie folgt:

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

Hinweis: Vergessen Sie nicht, das Nuget-Paket Microsoft.EntityFrameworkCore.InMemory Wie folgt zu installieren:

Installationspaket Microsoft.EntityFrameworkCore.InMemory

Für weitere Details: Testen mit InMemory

33
TanvirArjel

Versuchen Sie, meine Moq/NSubstitute-Erweiterung MockQueryable zu verwenden, um Zeit zu sparen: https://github.com/romantitov/MockQueryable unterstützt alle Sync/Async-Vorgänge

//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 wird ebenfalls unterstützt

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

Hinweis:

  • AutoMapper unterstützt ab Version 1.0.4
  • DbQuery wird ab Version 1.1.0 unterstützt
  • EF Core 3.0 wird ab Version 3.0.0 unterstützt
2
R.Titov

Der Fehler, den Sie erhalten, besteht darin, dass Sie die Movies-Eigenschaft in Ihrem Datenbankkontext als virtuell deklarieren müssen.

Wie in den Kommentaren erwähnt, sollten Sie zum Testen die eingebauten Speicheranbieter von EF verwenden.

0
Michael Brown