Add integration test infrastructure

- Create backend integration test project with xUnit
- Add required packages: TestHost, Mvc.Testing, EF InMemory, FluentAssertions
- Add project reference to backend API
- Create IntegrationTestBase class with WebApplicationFactory setup
- Install Supertest for HTTP integration testing
This commit is contained in:
Denis Urs Rudolph
2026-04-08 20:23:48 +02:00
parent d3ec22aa99
commit 3421818d41
17 changed files with 4916 additions and 0 deletions
@@ -0,0 +1,86 @@
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RacePlannerApi.Data;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Xunit;
namespace backend.Tests.Integration;
public class IntegrationTestBase : IClassFixture<WebApplicationFactory<Program>>
{
protected readonly WebApplicationFactory<Program> _factory;
protected readonly HttpClient _client;
protected readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
public IntegrationTestBase(WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
// Replace the database context with an in-memory database
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<RacePlannerDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
services.AddDbContext<RacePlannerDbContext>(options =>
{
options.UseInMemoryDatabase("IntegrationTestDb");
});
// Ensure the database is created and seeded
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<RacePlannerDbContext>();
db.Database.EnsureCreated();
});
});
_client = _factory.CreateClient();
}
protected HttpClient CreateAuthenticatedClient(string token = "")
{
var client = _factory.CreateClient();
if (!string.IsNullOrEmpty(token))
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}
return client;
}
protected async Task<T?> GetAsync<T>(string url)
{
var response = await _client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<T>(_jsonOptions);
}
protected async Task<TResponse?> PostAsync<TRequest, TResponse>(string url, TRequest data)
{
var response = await _client.PostAsJsonAsync(url, data, _jsonOptions);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<TResponse>(_jsonOptions);
}
protected async Task<TResponse?> PutAsync<TRequest, TResponse>(string url, TRequest data)
{
var response = await _client.PutAsJsonAsync(url, data, _jsonOptions);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<TResponse>(_jsonOptions);
}
protected async Task DeleteAsync(string url)
{
var response = await _client.DeleteAsync(url);
response.EnsureSuccessStatusCode();
}
}