C# 如何测试DbContext和fluentapi

C# 如何测试DbContext和fluentapi,c#,asp.net-core,asp.net-core-webapi,xunit,ef-core-2.1,C#,Asp.net Core,Asp.net Core Webapi,Xunit,Ef Core 2.1,在我的ASP.NET Core web应用程序中,我使用EF Core连接到SQL Server数据库。我有我的DbContext对象和一堆DbSets。我试图编写集成测试,基本上测试DbContext,它的表和关系 我从来没有做过这样的测试,所以我正在为如何做的决定而挣扎。我看了Jason Roberts PluralSight的课程“ASP.NET核心MVC测试基础”,但没有提到这样的测试。所以现在我不确定这样的测试在企业中是否普遍 我的目标是,只需单击一下,就可以100%确定连接字符串后面

在我的ASP.NET Core web应用程序中,我使用EF Core连接到SQL Server数据库。我有我的
DbContext
对象和一堆
DbSet
s。我试图编写集成测试,基本上测试
DbContext
,它的表和关系

我从来没有做过这样的测试,所以我正在为如何做的决定而挣扎。我看了Jason Roberts PluralSight的课程“ASP.NET核心MVC测试基础”,但没有提到这样的测试。所以现在我不确定这样的测试在企业中是否普遍

我的目标是,只需单击一下,就可以100%确定连接字符串后面的数据库与我的应用程序所期望的完全一致。我希望有带有FKs的表,最重要的是,我的fluent API配置也是正确的

以下是我今天的收获:

    [Fact]
    public void IncludeAuthor()
    {            
        var _context = new BookstoreContext(_connectionString); // Hardcoded in the test's constructor
        var book = _context.Books
            .Include(b => b.Author)
            .FirstOrDefault();     

        Assert.NotNull(book);
        Assert.NotNull(book.Author);
    }
这是处理此类测试的正确方法吗

我应该测试真实的数据库吗?(当然是开发数据库)

使用它可以帮助您模拟与数据库的交互,而无需实际与真实数据库交互

下面是关于如何在codeproject上的数据库中使用它的示例,它将通过示例向您解释

使用
Moq
的最佳实践是将其与依赖项一起使用 注射


Moq.

我通常不做集成测试,我使用Moq,但如果需要做集成测试,我通常测试是先添加、选择、更新然后删除。但我是唯一一个连接/使用它的人,因为如果正在使用它,将导致错误

添加测试

private int _addedId {get; set;}


var _context = new BookstoreContext(_connectionString);

var book = new Book
{
    Property1 = "BookName",
    Property2 = "BookSomething",
    Author = //add new or select from given
}

_context.Books.Add(book);
_context.SaveChanges();

var recentlyAddedBook = _context.Book.Include(b => b.Author).OrderByDescending(x => x.Id).FirstOrDefault();

Assert.AreEqual(book.Id, recentlyAddedBook.Id);
_addedId = book.Id;
选择测试

var book = _context.Books.FirstOrDefault(x => x.Id == _addedId);

Assert.AreEqual(book.Property1, "BookName");
Assert.IsNotNull(book);
更新测试

var book = _context.Books.FirstOrDefault(x => x.Id == _addedId);
book.PropertyName = "SomethingChange";
_context.SaveChanges();
//additional request to see if changes are really made in db
var updateBook = _context.Book.FirstOrDefault(x => x.Id == _addedId);

Assert.IsNotNull(updateBook);
Assert.AreEqual(book.PropertyName, updateBook.PropertyName);
删除测试

var book = _context.Books.FirstOrDefault(x => x.Id == _addedId);
_context.Remove(book);
_context.SaveChanges();

//get again and it should be null
var recentlyDeleted = _context.Books.FirstOrDefault(x => x.Id == _addedId);

Assert.IsNull(recentlyDeleted);

集成测试基本上是使用真实数据库进行的,如果您打算通过MOQ数据库进行测试,那么您可以使用以下方法:

  • Entityframework.InMemory包:违反引用完整性约束
  • Entityframework.SqlLite包:提供引用完整性约束
  • 使用方法如下

    在单独的类中实现FakeBookstoreContext

    public class FakeBookstoreContext : IDisposable
    {
        public BookstoreContext `enter code here`m_context;
    
        public FakeBookstoreContext()
        {
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();
    
            var options = new DbContextOptionsBuilder<BookstoreContext>()
                .UseSqlite(connection)
                .Options;
    
            m_context = new BookstoreContext(options);
            m_context.Database.EnsureCreated();
        }
    
        public void Dispose()
        {
            m_context.Database.EnsureDeleted();
    
            m_context.Dispose();
        }
    }
    
    公共类FakeBookstoreContext:IDisposable
    {
    公共书店上下文`在此处输入代码`m_上下文;
    公共假冒书店上下文()
    {
    var connection=newsqliteconnection(“数据源=:内存:”);
    connection.Open();
    var options=new DbContextOptionsBuilder()
    .UseSqlite(连接)
    .选择;
    m_context=新书店上下文(选项);
    m_context.Database.recreated();
    }
    公共空间处置()
    {
    m_context.Database.EnsureDeleted();
    m_context.Dispose();
    }
    }
    
    在集成测试中,注入上述FakeBookstoreContext,并按如下所示继续测试

    YourclassToTest y=新YourclassToTest(伪造书店上下文)

    y、 Methodname()


    注意:为FakeBookstoreContext创建实例

    如果进行集成测试,我通常会添加、选择、编辑和删除以查看更改是否有效。您知道如何在vs中调试吗?请记住,大多数测试框架,包括xunit,都将异步运行测试—其中许多测试一次运行。您应该关闭此功能,或者通过对数据库进行可能会破坏其他测试的修改来确保测试不会相互干扰。您可以将InMemoryDatabase用于这些测试MQ是一个很好的库,但使用它时会有一个非常重要的区别。您的fluent API将使用LinqToObject而不是LinqToSql。在许多情况下,这很好,但有时会有不同的效果。@JakubRusilko我不知道,谢谢你提供的信息。
    box/unbox/ConvertTo…
    将在
    Where
    中与moq一起工作,如果它在db上执行,则不在
    linq2sql
    上,但如果在内存上执行,则会工作
    public class FakeBookstoreContext : IDisposable
    {
        public BookstoreContext `enter code here`m_context;
    
        public FakeBookstoreContext()
        {
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();
    
            var options = new DbContextOptionsBuilder<BookstoreContext>()
                .UseSqlite(connection)
                .Options;
    
            m_context = new BookstoreContext(options);
            m_context.Database.EnsureCreated();
        }
    
        public void Dispose()
        {
            m_context.Database.EnsureDeleted();
    
            m_context.Dispose();
        }
    }