在开发项目的过程中,经常需要对数据库进行集成测试,以确保数据库约束正确配置并且能够正常工作。在这种需求下,希望能够找到一个既不会消耗太多处理能力,也不会在测试后留下太多清理工作的解决方案。
在寻找解决方案的过程中,偶然发现了一份文档,它提供了两种方法来实现内存中的数据库测试。经过对比,认为使用内存数据库的方式更适合,因为它不需要引入SQLite作为额外的依赖。
然而,在实际实施过程中,发现内存数据库并不满足需求,主要有两个原因:首先,内存数据库需要一个名称,这样才能在多个测试中使用同一个数据库。这对来说是一个麻烦,因为不希望在每个测试中重新创建数据库。其次,内存数据库并不是一个关系型数据库,它甚至不能算是一个真正的数据库,因为在配置的数据库上下文中,所有的约束都没有被验证。
基于以上原因,决定使用SQLite进行内存中的测试,这样可以复用功能,并且能够检查生成的查询。
首先,需要创建一个新的ASP.NET Core MVC项目,并启用个人身份验证。这是因为它已经配置好了数据库上下文,当然,也可以自己创建。
接下来,将创建一个.NET Core控制台项目作为测试项目。在这个测试项目中,将进行大部分的工作。
在测试项目中,需要引用Web应用程序项目,以便可以访问数据库上下文。然后,需要安装一些Nuget包:
现在已经准备好了所有需要的东西,接下来是实现部分。
按照习惯,在测试项目中,通常会创建一个名为TestUtilities的文件夹,并在其中创建一个名为TestDatabaseContextFactory的静态类。
下面是这个类的代码,已经逐行注释了每个部分的作用。
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 contextOptionsBuilder =
new DbContextOptionsBuilder();
LoggerFactory loggerFactory = new LoggerFactory();
loggerFactory.AddDebug();
loggerFactory.AddConsole();
SqliteConnectionStringBuilder connectionStringBuilder = new SqliteConnectionStringBuilder { Mode = SqliteOpenMode.Memory };
SqliteConnection connection = new SqliteConnection(connectionStringBuilder.ConnectionString);
connection.Open();
contextOptionsBuilder.UseLoggerFactory(loggerFactory);
contextOptionsBuilder.UseSqlite(connection);
contextOptionsBuilder.EnableSensitiveDataLogging();
ApplicationDbContext context = new ApplicationDbContext(contextOptionsBuilder.Options);
context.Database.EnsureCreated();
return context;
}
}
}
有了这个类,现在可以在测试中使用它,如下所示:
namespace Test
{
using NUnit.Framework;
using Test.TestUtilities;
using WebApplication.Data;
[TestFixture]
public class TestingDatabaseCreation
{
[Test]
public void TestCreation()
{
ApplicationDbContext context = TestDatabaseContextFactory.CreateDbContext();
}
}
}
尽管这个测试没有断言任何内容,但它仍然会通过,而且如果查看测试运行的输出,将看到所有对数据库执行的查询。
这可能看起来没什么,但它已经成为项目中的一个标准做法,因为尽管单元测试的概念很好,但有时测试需要小而快,但仍然需要执行实际的应用程序逻辑,尤其是当使用SQL时,功能不仅仅存在于应用程序中。