使用 EF Core 和内存数据库进行单元测试 [英] Unit testing with EF Core and in memory database
问题描述
我使用的是 ASP.NET Core 2.2、EF Core 和 MOQ.正如您在以下代码中看到的那样,我有两个测试,并且同时运行,两个数据库名称均为MovieListDatabase" 我在其中一个测试中出现错误并显示以下消息:
I am using ASP.NET Core 2.2, EF Core and MOQ. As you can see in the following code, I have two tests, and running both together, with both database name "MovieListDatabase" I got an error in one of the tests with this message:
Message: System.ArgumentException : An item with the same key has already
been added. Key: 1
如果我分别运行每一个,它们都会通过.
If I run each one separately they both pass.
而且,在两个测试中使用不同的数据库名称,例如MovieListDatabase1"和MovieListDatabase2",并且同时运行它再次通过.
And also, having a different database name in both tests, like "MovieListDatabase1" and "MovieListDatabase2" and running both together it pass again.
我有两个问题:为什么会发生这种情况?以及如何重构我的代码以在两个测试中重用内存数据库并使我的测试看起来更简洁?
I have two questions: Why does this happen? and how can I refactor my code to re-use the in-memory database in both tests and make my test to look a bit cleaner?
public class MovieRepositoryTest
{
[Fact]
public void GetAll_WhenCalled_ReturnsAllItems()
{
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(new 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))
{
var sut = new MovieRepository(context);
//Act
var movies = sut.GetAll();
//Assert
Assert.Equal(3, movies.Count());
}
}
[Fact]
public void Search_ValidTitlePassed_ReturnsOneMovie()
{
var filters = new MovieFilters { Title = "Movie 1", YearOfRelease = 2018, Genre = "Action" };
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(new 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))
{
var sut = new MovieRepository(context);
//Act
//var movies = _sut.Search(_filters);
var movies = sut.Search(filters);
//Assert
Assert.Single(movies);
}
}
}
这是存储库类
public class MovieRepository: IMovieRepository
{
private readonly MovieDbContext _moviesDbContext;
public MovieRepository(MovieDbContext moviesDbContext)
{
_moviesDbContext = moviesDbContext;
}
public IEnumerable<Movie> GetAll()
{
return _moviesDbContext.Movies;
}
public IEnumerable<Movie> Search(MovieFilters filters)
{
var title = filters.Title.ToLower();
var genre = filters.Genre.ToLower();
return _moviesDbContext.Movies.Where( p => (p.Title.Trim().ToLower().Contains(title) | string.IsNullOrWhiteSpace(p.Title))
& (p.Genre.Trim().ToLower().Contains(genre) | string.IsNullOrWhiteSpace(p.Genre))
& (p.YearOfRelease == filters.YearOfRelease | filters.YearOfRelease == null)
);
}
}
谢谢
推荐答案
看起来你可能想要一个 课件.
It looks like you might want a class fixture.
何时使用:当您想要创建单个测试上下文并在类中的所有测试之间共享它时,并在类中的所有测试完成后将其清理干净.
When to use: when you want to create a single test context and share it among all the tests in the class, and have it cleaned up after all the tests in the class have finished.
创建一个单独的类来设置您的测试将共享的任何数据,并在测试运行完成后对其进行清理.
Create a separate class to setup whatever data your tests will share, and to clean it up when the tests are finished running.
public class MovieSeedDataFixture : IDisposable
{
public MovieDbContext MovieContext { get; private set; } = new MovieDbContext();
public MovieSeedDataFixture()
{
MovieContext.Movies.Add(new Movie { Id = 1, Title = "Movie 1", YearOfRelease = 2018, Genre = "Action" });
MovieContext.Movies.Add(new Movie { Id = 2, Title = "Movie 2", YearOfRelease = 2018, Genre = "Action" });
MovieContext.Movies.Add(new Movie { Id = 3, Title = "Movie 3", YearOfRelease = 2019, Genre = "Action" });
MovieContext.SaveChanges();
}
public void Dispose()
{
MovieContext.Dispose();
}
}
然后通过扩展 IClassFixture
接口在您的测试中使用它.
Then use it in your tests by extending the IClassFixture<T>
interface.
public class UnitTests : IClassFixture<MovieSeedDataFixture>
{
MovieSeedDataFixture fixture;
public UnitTests(MovieSeedDataFixture fixture)
{
this.fixture = fixture;
}
[Fact]
public void TestOne()
{
// use fixture.MovieContext in your tests
}
}
这篇关于使用 EF Core 和内存数据库进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!