Как поделиться в базе данных памяти, используя TestServer и класс заполнения данных

Недавно я начал использовать класс TestServer для самостоятельного размещения и загрузки Aspnet Core API для запуска интеграционных тестов без выделенной рабочей среды.

Мне нравится, как он работает, и используя имя пользовательской среды, я решил контролировать способ создания контекста EF, переключаясь с SQL Server на базу данных In-Memory.

Проблема в том, что для заполнения данных, необходимых для запуска тестов через запросы API, было бы очень дорого как для кодирования, так и для времени выполнения.

Моя идея состоит в том, чтобы создать класс или простую структуру для заполнения данных, необходимых для каждого теста, но для этого мне нужно использовать ту же базу данных в памяти, которая инициализируется с помощью стека API с помощью TestServer.

Как это можно сделать?

1 ответ

Решение

В первую очередь важно понимать, что для лучшего тестирования при замене реляционной базы данных, такой как SQL Server, база данных In Memory не идеальна.

Среди различных ограничений он не поддерживает ограничения внешнего ключа. Лучший способ использовать базу данных в памяти - использовать режим SQLite 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);
    }
}

Обработка данных (класс Helper)

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>());
    }
 }

Ключ - строка файла URI, используемая для создания соединения SQLite:var connection = new SqliteConnection("DataSource='file::memory:?cache=shared'");

Другие вопросы по тегам