创建单元测试时,需要用提供测试数据的测试类替换依赖项。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(){//arrangevar booksService = new BooksService(_booksContext);//actvar topBooks = booksService.GetTopBooksByPublisher(PublisherName);//assertAssert.Equal(10,topBooks.Count());}