Я работаю над веб-API ASP.NET Core 7 и пишу интеграционные тесты с использованием CustomWebApplicationFactory.
Моя программа выполняет несколько внешних HTTP-вызовов , поэтому я хочу посмеяться над ними в своих тестах. Я хочу иметь детальный контроль над тем, что я имитирую, и иметь возможность настраивать возвращаемые значения HTTP для каждого теста.
Вот почему я использую WithWebHostBuilder, а затем ConfigurationTestServices чтобы издеваться над HttpClient.
Моя проблема в том, что при простом добавлении вызова Factory.WithHostBuilder тесты начинают давать сбой, как если бы база данных ничего не сохраняется.
Я покажу вам упрощенный пример, который имитирует мой подход и в котором описанная мной проблема сохраняется.
Моя простая фабрика веб-приложений, заменяющая DbContext:
public class CustomWebApplicationFactory : WebApplicationFactory where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
ServiceDescriptor? descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions));
if (descriptor is not null)
{
services.Remove(descriptor);
}
ServiceDescriptor? dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
if (dbConnectionDescriptor != null)
{
services.Remove(dbConnectionDescriptor);
}
// Create open SqliteConnection so EF won't automatically close it.
services.AddSingleton(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext((container, options) =>
{
DbConnection connection = container.GetRequiredService();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("test");
}
}
Мои тесты используют эту фабрику:
public class SetsTests : IClassFixture
{
private readonly CustomWebApplicationFactory _factory;
public SetsTests(CustomWebApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task Get_WithTwoSets_ReturnsJsonWithThem()
{
HttpClient client = _factory.CreateClient();
await using (AsyncServiceScope scope = _factory.Services.CreateAsyncScope())
{
ISetsRepository setsRepository = scope.ServiceProvider.GetRequiredService();
SetDto set1 = await setsRepository.CreateSetAsync("123");
SetDto set2 = await setsRepository.CreateSetAsync("456");
}
HttpResponseMessage response = await client.GetAsync("api/sets/");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var respType = response.Content.Headers.ContentType?.MediaType;
Assert.Equal("application/json", respType);
SetDto[]? content = await GetResponseContentAsync(response);
Assert.NotNull(content);
Assert.Equal(2, content.Length);
}
}
Этот простой тест пройден — я создаю 2 набора, а затем они правильно возвращаются из БД.
Но только с одним простым изменением (внедрить ложные сервисы), тесты Assert.Equal(2, content.Length); теперь завершаются неудачно; проверка — возвращаемый контент содержит 0 записей.
// code breaks if I replace
// HttpClient client = _factory.CreateClient();
// with
HttpClient client = _factory.WithWebHostBuilder(builder => { }).CreateClient();
Я понятия не имею, почему это вызывает это.
Мой Program.cs в моем API в этом примере очень прост:< /p>
using BrickFolio.Data;
using Microsoft.EntityFrameworkCore;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddDataServices(builder.Configuration, builder.Environment);
WebApplication app = builder.Build();
if (builder.Environment.IsProduction() is false)
{
using IServiceScope scope = app.Services.CreateScope();
IServiceProvider services = scope.ServiceProvider;
try
{
SetsContext context = services.GetRequiredService();
context.Database.Migrate();
}
catch (Exception ex)
{
ILogger logger = services.GetRequiredService();
logger.LogError(ex, "An error occurred while migrating the database.");
}
}
app.MapControllers();
app.Run();
public partial class Program
{
}
Он только устанавливает контроллеры и вызывает AddsDataServices из проекта данных:
public static IServiceCollection AddDataServices(this IServiceCollection services, IConfiguration config, IHostEnvironment env)
{
DatabaseOptions? databaseOptions = config.GetRequiredSection(DatabaseOptions.SectionName)
.Get();
if (databaseOptions is null)
{
throw new ArgumentException("DatabaseOptions not found in configuration");
}
var dbPath = Path.Combine(env.ContentRootPath, databaseOptions.FileName);
services.AddDbContext(options =>
options.UseSqlite($"Data Source={dbPath}"));
services.AddScoped();
return services;
}
Это добавляет DbContext.
Я неправильно понимаю WithHostBuilder? Почему это привело к такому неожиданному поведению?
Я пробовал не использовать репозиторий, а просто использовать контексты в своих тестах, но результат был тот же.
Я также попробовал немного отладить его и использовать SQLite с файлами вместо параметра памяти. Я назначил уникальный Guid тестовой базе данных в каждом тесте:
services.AddSingleton(container =>
{
var dbFileName = $"test-{Guid.NewGuid()}.db";
var connection = new SqliteConnection($"Data Source={dbFileName}");
connection.Open();
return connection;
});
В результате я знаю, что без вызова WithHostBuilder он создает только один тестовый файл, содержащий записи.
Когда я вызовите WithHostBuilder, я получу два файла: один с записями, а второй пустой. Я подозреваю, что то же самое происходит, когда я использую источник данных в памяти - в итоге у меня есть два подключения к базе данных «в памяти», причем тесты используют одно соединение, а программа API - другое.
Я только не знаю, как с этим справиться.
РЕДАКТИРОВАТЬ: я понял, что не показал свой контроллер:
[ApiController]
[Route("api/sets")]
public class SetsController : ControllerBase
{
private readonly ISetsRepository _setsRepository;
public SetsController(ISetsRepository setsRepository)
{
_setsRepository = setsRepository;
}
// GET api/sets
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[Produces("application/json")]
public async Task Index()
{
IList sets = await _setsRepository.GetAllSetsAsync();
return sets;
}
}
Подробнее здесь: https://stackoverflow.com/questions/786 ... tabase-con
Интеграционные тесты веб-API ASP.NET Core 7 — WithHostBuilder разрывает соединение с базой данных ⇐ C#
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение