Imaginemos que tienes un proyecto de aplicación moderno hecho en la época del lanzamiento de .NET core 6. No hace falta decir que su estructura es un poco diferente de las versiones anteriores de .net (donde tenías 2 archivos separados - Startup.cs y Program.cs)
Repasemos las diferencias entre la estructura antigua y la nueva, que serán útiles para explicaciones posteriores
Estructura de proyecto en la época de .NET Core 5
Startup.cs
Aquí solíamos registrar todos los servicios, configuraciones y clases.
De hecho, este es el punto de partida de la aplicación. Aquí, por ejemplo, añadí unas pocas líneas de código para crear un scope para el Entity Context y ejecutar las migraciones iniciales de la base de datos.
Los dos archivos antiguos Program.cs y Startup.cs ahora están fusionados en un único archivo Program.cs, y ya no hay ninguna clase dentro. Solo funciones puras para añadir nuevas modificaciones.
Código típico de Program.cs en un proyecto .NET Core 6+
var builder = WebApplication.CreateBuilder(args);//...builder.Services.AddControllers();// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbucklebuilder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();var app = builder.Build();//...if(app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI();}app.UseAuthorization();
Estructura .NET Core 5 vs 6 - ventajas y desventajas (+ resolución de problemas)
Ventajas
1 archivo simple en lugar de 2
menos envoltorios de clases - código más simple y limpio
se presentó el nuevo webApplicationBuilder
Desventajas
No tenemos ninguna clase en el programa para crear relaciones. Por ejemplo, para añadir librerías como Mediator o FluentValidation necesitas obtener el tipo del assembly por nombre, ejemplo:
Resolver el nombre del assembly raíz en .NET Core 6
Una de las posibles soluciones es añadir esta línea al final del archivo Program.csz:
// Make the implicit Program class public so test projects can access itpublicpartialclassProgram{}
Ahora puedes usar estructuras como typeof(Program) o WebApplicationFactory<Program> (al hacer pruebas)
Configurar el Test server de .NET Core 5
Aquí tenemos un paquete nu-get llamado Microsoft.TestPlatform.TestHost junto con Microsoft.AspNetCore.TestHost.
Aquí está el TestServerFixture de uno de mis proyectos, que se relaciona con el Program de la aplicación real:
publicclassTestServerFixture:IDisposable{privatereadonlyTestServer _testServer;publicHttpClient Client {get;}publicTestServerFixture(){var builder =newWebHostBuilder()// .UseContentRoot(GetContentRootPath()).UseEnvironment("Development").UseConfiguration(FakeConfiguration.GetInstance()).UseStartup<Startup>();// Uses Start up class from your API Host project to configure the test server _testServer =newTestServer(builder); Client = _testServer.CreateClient();}// ...}
Las pruebas unitarias para controladores y utilidades son un buen método, pero también prefiero tener una instancia real en ejecución de la aplicación para mis pruebas, con la capacidad de enviar peticiones POST y GET reales.
Así que mucha gente en internet empieza a construir otra configuración separada para ejecutar ese nodo virtual de pruebas, pero en realidad ya tenemos todas las configuraciones necesarias en nuestro archivo Program.cs. ¡Usémoslo! Pero necesitamos hacer algunos cambios en él, por ejemplo - usar no una base de datos física real, sino una en memoria.
Antes que nada, ahora podemos relacionarnos con la clase Program.cs y usarla en combinación con la clase WebApplicationFactory:
Probar una petición GraphQL HotChocolate a través de pruebas unitarias
Tomemos el caso más exigente - ejecutar una mutación GraphQL a través de una aplicación muy cercana a la real.
Usaré la misma clase TestServerFixture aquí:
[Fact]public async Task TestRegister_OnSuccess_ReturnsUser(){// arrange var query = @"
mutation register{register( payload:{ email:""test@t10.com"" password:""1A?a456"" passwordConfirmation:""1A?a456"" ruleAgreement: true
}){ id
}} ";// act var request = QueryRequestBuilder.New().SetQuery(query).Create(); var result = await WebApplicationFactory.Services.ExecuteRequestAsync(request); var json = await result.ToJsonAsync();// assert Assert.Null(result.Errors); Assert.Contains("data", json); Assert.Contains("id", json); Assert.Matches( @"(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}", json);}
Este es un ejemplo completamente funcional, que trabaja con nuestro fixture de test server y, de esa manera, con la base de datos virtual de EntityFramework inyectada.