创建单元测试时,需要用提供测试数据的测试类替换依赖项。Entity Framework Core(EF Core)上下文中的依赖项又如何呢?通常没有一个接口是由上下文实现的,但是上下文本身(例如,BookContext)是被注入的。EF Core基于内存的提供程序提供了一个解决方案,可以将其用作模拟类,而不是使用EF Core SQL Server提供程序。.
下面从一个简单的Book类型、BooksContext和BooksServer开始。BooksService类应该在单元测试中进行测试。
Book是一个简单的类,它保留了一些属性:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public string Publisher { get; set; }
}
类BooksContext管理到数据库的连接,并将Book类型影射到Books表:
public class BooksContext : DbContext
{
public BooksContext(DbContextOptions<BooksContext> options)
: base(options) { }
public DbSet<Book> Books { get; set; }
}
最后,类BooksService通过依赖注入使用BooksContext,并定义GetTopBooksByPublisher方法。这个方法应该只返回10本书:
public class BooksServices
{
private BooksContext _booksContext;
public BooksServices(BooksContext booksContext)
{
_booksContext = booksContext;
}
public IEnumerable<Book> GetTopBooksByPublisher(string publisher)
{
if (publisher == null)
{
throw new ArgumentNullException(nameof(publisher));
}
return _booksContext.Books
.Where(b => b.Publisher == publisher)
.Take(10)
.ToList();
}
}
对于单元测试,创建一个xUnit项目。为了使用EF Core内存提供程序,除了添加EFCoreSample项目之外,还要添加NuGet包Microsoft.EntityFrameworkCore.InMemory。
现在,可以使用DbContextOptionsBuilder来创建内存中数据库的选项。UseInMemoryDatabase是包Microsoft.EntityFrameworkCore.InMemory中的扩展方法,用于添加EF Core内存提供程序。在InitContext方法中,将创建1000个book对象,并保存到上下文的对象列表中,以备单元测试使用:
public class BooksServiceTest:IDisposable
{
public void Dispose()
{
_booksContext?.Dispose();
}
private BooksContext _booksContext;
private const string PublisherName = "A Publishers";
public BooksServiceTest()
{
InitContext();
}
private void InitContext()
{
var builder = new DbContextOptionsBuilder<BooksContext>()
.UseInMemoryDatabase("BooksDB");
_booksContext = new BooksContext(builder.Options);
var books = Enumerable.Range(1, 1000)
.Select(b => new Book
{
BookId = b,
Title = $"Sample {b}",
Publisher = PublisherName
})
.ToList();
_booksContext.Books.AddRange(books);
_booksContext.SaveChanges();
}
}
现在,单元测试方法GetTopBooksByPublisherCount在arrange部分实例化BooksService,在act部分调用GetTopBooksByBuilder方法,最后在assert部分检查返回的图书数量:
[Fact]
public void GetBooksByPublisherCount()
{
//arrange
var booksService = new BooksService(_booksContext);
//act
var topBooks = booksService.GetTopBooksByPublisher(PublisherName);
//assert
Assert.Equal(10,topBooks.Count());
}