ASP.NET Core 2.2、EF Core、MOQを使用しています。テストを実行すると、次のエラーが発生します。
メッセージ:System.NotSupportedException:非仮想(VBでオーバーライド可能)メンバーでの無効なセットアップ:x => x.Movies
私は何を間違っていますか?
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;
}
}
そして、これはMovie
dbSetを使用した私のDBコンテキストです。
public class MovieDbContext: DbContext
{
public MovieDbContext(DbContextOptions<MovieDbContext> options) : base(options)
{
}
public DbSet<Movie> Movies { get; set; }
}
そして、テストされるメソッドGetAll
を持つリポジトリ:
public class MovieRepository: IMovieRepository
{
private readonly MovieDbContext _moviesDbContext;
public MovieRepository(MovieDbContext moviesDbContext)
{
_moviesDbContext = moviesDbContext;
}
public IEnumerable<Movie> GetAll()
{
return _moviesDbContext.Movies;
}
}
DbContext
でEFコアMovieRepository
を使用しているようです。したがって、モックを使用する代わりに、EF Core InMemory
データベースを使用することは、優れたオプションになります。これにより、複雑さも軽減されます。
次のようにGetAllTest()
メソッドを記述します。
[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);
}
}
注:次のようにMicrosoft.EntityFrameworkCore.InMemory
nugetパッケージをインストールすることを忘れないでください:
インストールパッケージMicrosoft.EntityFrameworkCore.InMemory
詳細: InMemoryを使用したテスト
時間を節約するには、Moq/NSubstitute拡張MockQueryableを使用してみてください: https://github.com/romantitov/MockQueryable すべての同期/非同期操作をサポート
//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もサポート
//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);
注:
表示されるエラーは、dbcontextのMoviesプロパティをVirtualとして宣言する必要があるためです。
誰かがコメントで指摘したように、テストにはEFの組み込み memory provider を使用する必要があります。