65.9K
CodeProject 正在变化。 阅读更多。
Home

测试 Entity Framework Core

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2018年11月13日

CPOL

4分钟阅读

viewsIcon

5436

如何使用 Entity Framework Core 编写自动化测试。

引言

在这篇文章中,我们将看看如何使用 Entity Framework Core 编写自动化测试。

就像之前的文章一样,代码可以在 这里找到。

故事

不久前,在做一个项目时,我想用一个模拟数据库做一个集成测试,这样我就可以验证数据库约束实际上是否有效,并且配置是否正确。

因此,为了找到一个可行的解决方案,既能完成工作,又不会在处理能力和清理方面带来太多开销,我最终找到了 这个文档页面

如果您浏览了文档页面,您会发现该文档实际上为此指定了两种方法。
所以在浏览了这两种方法之后,我认为 InMemory 方法对我来说更好,因为我不想包含对 SQLite 的额外依赖。
嗯,在按照例子实现之后,我发现它实际上并不是我所需要的,这主要是因为两个主要原因

  • InMemory 数据库需要一个名称。 这样做的原因是为了让您可以在测试中使用同一个数据库。这对我来说主要是个麻烦,因为我不想在每个测试中都重新创建数据库,如果我能避免重复,那就更好了。
  • InMemory 数据库不是关系数据库,它根本算不上是一个数据库,因为我艰难地发现,我配置到数据库上下文中的所有约束甚至都没有被验证。 如果我没有遵循测试驱动开发方法,那么我会在手动测试或更糟糕的情况下(在生产环境中)发现我的代码中的一个错误。

这引出了这篇文章的主题,使用 SQLite 进行内存测试,重用功能,甚至检查生成的查询。

设置

首先,我们将创建一个新的 ASP.NET Core MVC 项目,该项目具有单独的身份验证(这主要是因为它已经配置了数据库上下文,当然,您可以构建自己的)。

下一步,也是我们将完成大部分工作的地方,是测试项目。 为此,我们需要一个 .NET Core 控制台项目。

一旦我们有了测试项目并引用了我们的 WebApplication 项目(以便我们可以访问 DbContext),我们就需要安装一些 Nuget 包

  • Microsoft.NET.Test.Sdk (15.7.2)
    • 这是运行单元测试所必需的。
  • NUnit (3.10.1)
    • 我选择的测试框架,尽管您可以使用任何适合您需求的框架。
  • NUnit3TestAdapter (3.10)
    • 这样 ReSharper 和 Visual Studio 就可以找到并运行测试。
  • Microsoft.AspNetCore.App
    • 安装此包是因为我们使用了 IdentityDbContext,但这仅仅是因为我为这个例子选择使用的模板。
  • Microsoft.EntityFrameworkCore.Sqlite (2.1.0)

实现

现在我们拥有了我们需要的一切,让我们继续实现。

出于习惯,在测试项目内部,我通常创建一个名为 TestUtilities 的文件夹,并在其中创建一个名为 TestDatabaseContextFactorystatic 类。

我逐行注释了该类,以解释每件事的作用。

namespace Test.TestUtilities
{
    using Microsoft.Data.Sqlite;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Logging;

    using WebApplication.Data;

    public static class TestDatabaseContextFactory
    {
        public static ApplicationDbContext CreateDbContext()
        {
            DbContextOptionsBuilder<ApplicationDbContext> 
                    contextOptionsBuilder = // declaring the options we are going to use 
                                            // for the context we will be using for tests
                new DbContextOptionsBuilder<ApplicationDbContext>();

            LoggerFactory loggerFactory = new LoggerFactory(); // this will allow us to add
             // loggers so we can actually inspect what code and queries EntityFramework produces.
            loggerFactory
                .AddDebug() // this logger will log to the Debug output
                .AddConsole(); // this logger will output to the console

            SqliteConnectionStringBuilder connectionStringBuilder = 
              new SqliteConnectionStringBuilder { Mode = SqliteOpenMode.Memory }; // this is more 
                  // syntax friendly approach to defining and InMemory connection string for our 
                  // database, the alternative is to write it out as a string.
            SqliteConnection connection = 
                new SqliteConnection(connectionStringBuilder.ConnectionString); // create a 
                                                        // connection to the InMemory database.
            connection.Open(); // open the connection

            contextOptionsBuilder.UseLoggerFactory(loggerFactory); // register the loggers 
                 // inside the context options builder, this way, entity framework logs the queries
            contextOptionsBuilder.UseSqlite(connection); // we're telling entity framework 
                                                         // to use the SQLite connection we created.
            contextOptionsBuilder.EnableSensitiveDataLogging(); // this will give us more 
                 // insight when something does go wrong. It's ok to use it here since it's a 
                 // testing project, but be careful about enabling this in production.

            ApplicationDbContext context = 
            new ApplicationDbContext(contextOptionsBuilder.Options); // creating the actual DbContext

            context.Database.EnsureCreated(); // this command will create the schema 
              // and apply configurations we have made in the context, like relations and constraints

            return context; // return the context to be further used in tests.
        }
    }
}

有了这个,我们现在就可以在我们的测试中使用它,如下所示

namespace Test
{
    using NUnit.Framework;

    using Test.TestUtilities;

    using WebApplication.Data;

    [TestFixture]
    public class TestingDatabaseCreation
    {
        [Test]
        public void TestCreation()
        {
            ApplicationDbContext context = TestDatabaseContextFactory.CreateDbContext();
        }
    }
}

即使这个测试没有断言任何东西,它仍然会通过,并且附带的好处是,如果我们查看测试运行的输出,我们将看到针对数据库运行的所有查询。

结论

这可能看起来不多,但它已成为我项目中使用的标准,因为即使单元测试的想法很好,但有时我们的测试需要小而快,但仍然执行实际的应用程序逻辑,这通常尤其是当使用 SQL 时,功能不仅仅存在于应用程序内部。

请记住,这种方法使用 SQLite,因此如果您使用其他数据库或 NoSQL 数据库,请查看它们是否具有内存中变体。 话虽如此,SQLite 和 SQL Server 也是不同的,但我们使用 Entity Framework 的 9/10 功能都存在于两者中。

我希望您喜欢这篇文章,并且未来还有一些其他与 Entity Framework Core 相关的文章。

祝好!

© . All rights reserved.