如何使用 TestServer 和数据种子类共享内存数据库以在内存中运行集成测试



我最近开始使用TestServer类自托管和引导Aspnet Core API来运行集成测试,而无需专用的运行环境。

我喜欢它的工作方式和使用自定义环境名称,我决定控制 EF 上下文的创建方式,从 SQL Server 切换到内存中数据库。

问题在于,为了通过API请求播种运行测试所需的数据,对于编码和运行时间来说都是非常昂贵的。

我的想法是创建一个类或一个简单的框架来播种每个测试所需的数据,但要做到这一点,我需要使用相同的内存数据库,该数据库由 TestServer 使用 API 堆栈初始化。

怎么可能呢?

首先,重要的是要了解,为了更好地测试以替换关系数据库(如SQL Server(,内存中数据库并不理想。

在各种限制中,它不支持外键约束。使用内存中数据库的更好方法是使用 SQLite 内存中模式。

以下是我用于设置测试服务器、设定数据种子并为依赖注入注册数据库上下文的代码:

测试服务器

public class ApiClient {
    private HttpClient _client;
    public ApiClient()
    {
        var webHostBuilder = new WebHostBuilder();
        webHostBuilder.UseEnvironment("Test");
        webHostBuilder.UseStartup<Startup>();
        var server = new TestServer(webHostBuilder);
        _client = server.CreateClient();
    }
    public async Task<HttpResponseMessage> PostAsync<T>(string url, T entity)
    {
        var content = new StringContent(JsonConvert.SerializeObject(entity), Encoding.UTF8, "application/json");
        return await _client.PostAsync(url, content);
    }
    public async Task<T> GetAsync<T>(string url)
    {
        var response = await _client.GetAsync(url);
        response.EnsureSuccessStatusCode();
        var responseString = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseString);
    }
}

数据种子设定(帮助程序类(

public class TestDataConfiguration
{
    public static IMyContext GetContex()
    {
        var serviceCollection = new ServiceCollection();
        IocConfig.RegisterContext(serviceCollection, "", null);
        var serviceProvider = serviceCollection.BuildServiceProvider();
        return serviceProvider.GetService<IMyContext>();
    }
}

数据种子设定(测试类(

[TestInitialize]
public void TestInitialize()
{
    _client = new ApiClient();
    var context = TestDataConfiguration.GetContex();
    var category = new Category
    {
        Active = true,
        Description = "D",
        Name = "N"
    };
    context.Categories.Add(category);
    context.SaveChanges();
    var transaction = new Transaction
    {
        CategoryId = category.Id,
        Credit = 1,
        Description = "A",
        Recorded = DateTime.Now
    };
    context.Transactions.Add(transaction);
    context.SaveChanges();
}

数据库上下文注册(在 IocConfig.cs 中(

public static void RegisterContext(IServiceCollection services, string connectionString, IHostingEnvironment hostingEnvironment)
{
    if (connectionString == null)
        throw new ArgumentNullException(nameof(connectionString));
    if (services == null)
        throw new ArgumentNullException(nameof(services));
    services.AddDbContext<MyContext>(options =>
    {
        if (hostingEnvironment == null || hostingEnvironment.IsTesting())
        {
            var connection = new SqliteConnection("DataSource='file::memory:?cache=shared'");
            connection.Open();
            options.UseSqlite(connection);
            options.UseLoggerFactory(MyLoggerFactory);
        }
        else
        {
            options.UseSqlServer(connectionString);
            options.UseLoggerFactory(MyLoggerFactory);
        }
    });
    if (hostingEnvironment == null || hostingEnvironment.IsTesting())
    {
        services.AddSingleton<IMyContext>(service =>
        {
            var context = service.GetService<MyContext>();
            context.Database.EnsureCreated();
            return context;
        });
    } else {
        services.AddTransient<IMyContext>(service => service.GetService<MyContext>());
    }
 }

键是用于创建 SQLite 连接的 URI 文件字符串: var connection = new SqliteConnection("DataSource='file::memory:?cache=shared'");

最新更新