ITLab.Template.DevPack.Testing
Helpers de teste shipados como pacote NuGet auxiliar para projetos consumidores do ITLab.Template.DevPack. Resolve três fricções recorrentes em testes de integração:
- Provider conflict quando o app de produção registra SqlServer/Postgres e o teste quer EF Core InMemory —
UseIsolatedInMemoryProvidermantém um service provider EF separado para o InMemory. - Authentication scaffolding —
TestAuthenticationHandleraceita roles via headerX-Test-Rolese produz umClaimsPrincipalautenticado com essas roles. - WebApplicationFactory boilerplate —
WebApplicationFactoryBase<TProgram, TDbContext>encapsula DB name único por instância, substituição doDbContextOptions, registro do test auth scheme e helpers de cliente (CreateClientWithRoles,CreateAnonymousClient).
Namespace:
using ITLab.Template.DevPack.Testing;(factory base e ponto de entrada),using ITLab.Template.DevPack.Testing.Authentication;(handler) eusing ITLab.Template.DevPack.Testing.Persistence;(UseIsolatedInMemoryProvider).
Componentes
| Tipo | Nome | Responsabilidade |
|---|---|---|
| Abstract Class | WebApplicationFactoryBase<TProgram, TDbContext> | WebApplicationFactory parametrizada — substitui DbContextOptions<TDbContext>, registra TestAuthenticationHandler como esquema default, expõe CreateClientWithRoles / CreateAnonymousClient e o hook ConfigureAdditionalServices |
| Sealed Class | TestAuthenticationHandler | Lê roles do header X-Test-Roles, emite ClaimsPrincipal autenticado |
| Static Class | IsolatedInMemoryProviderExtensions | DbContextOptionsBuilder.UseIsolatedInMemoryProvider(databaseName) — InMemory com IServiceProvider interno isolado |
| Class | CurrentUserBuilder | Builder fluente para mock de ICurrentUser (Moq) |
| Static Class | DateTimeProviderBuilder | Helpers para mock de IDateTimeProvider |
| Static Class | ObjectArrayExtensions | Helpers de assertion sobre object[] (extração tipada) |
Como usar
1. Criar a factory específica do projeto
public sealed class MyAppIntegrationTestFactory
: WebApplicationFactoryBase<Program, AppDbContext>
{
protected override void ConfigureAdditionalServices(IServiceCollection services)
{
// Mocks/fakes específicos do projeto, se houver.
services.RemoveAll<IExternalApiClient>();
services.AddSingleton<IExternalApiClient>(new FakeExternalApiClient());
}
}
2. xUnit fixture + cliente com roles
public class CriarPedidoEndpointTests : IClassFixture<MyAppIntegrationTestFactory>
{
private readonly MyAppIntegrationTestFactory _factory;
public CriarPedidoEndpointTests(MyAppIntegrationTestFactory factory)
=> _factory = factory;
[Fact]
public async Task Post_AsAuthorized_Returns201()
{
var client = _factory.CreateClientWithRoles("Pedidos.Create");
var response = await client.PostAsJsonAsync("/pedidos", new { Cliente = "Acme", Total = 100 });
response.StatusCode.Should().Be(HttpStatusCode.Created);
}
[Fact]
public async Task Post_AsAnonymous_Returns401()
{
var client = _factory.CreateAnonymousClient();
var response = await client.PostAsJsonAsync("/pedidos", new { Cliente = "Acme", Total = 100 });
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}
}
3. Usando UseIsolatedInMemoryProvider diretamente
A WebApplicationFactoryBase já chama internamente. Use diretamente quando estiver fora do contexto de WebApplicationFactory (ex.: testes de repositório isolados):
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseIsolatedInMemoryProvider("test-db")
.Options;
using var db = new AppDbContext(options);
O que não fazer
- Não importar este pacote em código de produção — tem
Microsoft.AspNetCore.Mvc.TestingeMoqcomo deps transitivas. - Não habilitar o
TestAuthenticationHandlerem ambientes de produção (não é gated automaticamente; a inclusão é responsabilidade doWebApplicationFactoryBase, que só roda em testes).
Extensão pelo produto
- Subclasses de
WebApplicationFactoryBasepodem sobrescreverConfigureAdditionalServicespara registrar mocks/fakes específicos. - O
claimsMapperopcional doDevPackIdentityContextMiddleware(no pacote principal) é independente doTestAuthenticationHandler— em testes, o handler já produz claims compatíveis com o mapper default.