使用EF Core进行单元测试

创建单元测试时,需要用提供测试数据的测试类替换依赖项。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());}