如何使用TestServer和数据种子类

时间:2018-03-11 21:50:15

标签: c# asp.net-core integration-testing in-memory-database asp.net-core-testhost

我最近开始使用TestServer类进行自托管,并在没有专用运行环境的情况下引导Aspnet Core API运行Integration Tests

我喜欢它的工作方式并使用自定义环境名称,我决定控制EF Context的创建方式,从SQL Server切换到In-Memory数据库。

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

我的想法是创建一个类或一个简单的框架来为每个测试所需的数据提供种子,但为此我需要使用相同的内存数据库,该数据库由{{3}的API堆栈初始化}。

怎么可能这样做?

1 个答案:

答案 0 :(得分:0)

首先要了解的是,为了更好地测试替代关系数据库这样的SQL Server,TestServer数据库并不理想。

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

以下是我用来设置TestServer,为数据播种并为依赖注入注册数据库上下文的代码:

<强> TESTSERVER

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连接的SQLite In-Memory modevar connection = new SqliteConnection("DataSource='file::memory:?cache=shared'");