Configuración del proyecto
Contexto
Esta guía se basa en la documentación de Microsoft sobre Pruebas de integración en ASP.NET Core. La muestra original utiliza una base de datos SQLite en memoria como almacenamiento de respaldo para las pruebas de integración. Reemplazarás SQLite por una instancia real de Microsoft SQL Server que se ejecuta en un contenedor Docker utilizando Testcontainers.
Puedes encontrar la muestra de código original en el repositorio dotnet/AspNetCore.Docs.Samples.
Clona el repositorio
Clona el repositorio de la guía de Testcontainers y accede al directorio del proyecto:
$ git clone https://github.com/testcontainers/tc-guide-testing-aspnet-core.git
$ cd tc-guide-testing-aspnet-core
Estructura del proyecto
La solución contiene dos proyectos:
RazorPagesProject.sln
├── src/RazorPagesProject/ # Aplicación ASP.NET Core Razor Pages
└── tests/RazorPagesProject.Tests/ # Pruebas de integración de xUnitProyecto de aplicación
El proyecto de aplicación (src/RazorPagesProject/RazorPagesProject.csproj) es una aplicación web Razor Pages que utiliza Entity Framework Core con SQLite como su proveedor de base de datos predeterminado:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>El ApplicationDbContext almacena entidades Message y proporciona métodos para consultarlas y gestionarlas:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Message> Messages { get; set; }
public async virtual Task<List<Message>> GetMessagesAsync()
{
return await Messages
.OrderBy(message => message.Text)
.AsNoTracking()
.ToListAsync();
}
public async virtual Task AddMessageAsync(Message message)
{
await Messages.AddAsync(message);
await SaveChangesAsync();
}
public async virtual Task DeleteAllMessagesAsync()
{
foreach (Message message in Messages)
{
Messages.Remove(message);
}
await SaveChangesAsync();
}
public async virtual Task DeleteMessageAsync(int id)
{
var message = await Messages.FindAsync(id);
if (message != null)
{
Messages.Remove(message);
await SaveChangesAsync();
}
}
public void Initialize()
{
Messages.AddRange(GetSeedingMessages());
SaveChanges();
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "You're standing on my scarf." },
new Message(){ Text = "Would you like a jelly baby?" },
new Message(){ Text = "To the rational mind, nothing is inexplicable; only unexplained." }
};
}
}Proyecto de pruebas
El proyecto de pruebas (tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj) incluye xUnit, la infraestructura de pruebas de ASP.NET Core y el módulo MSSQL de Testcontainers:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.17.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="Testcontainers.MsSql" Version="3.0.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\RazorPagesProject\RazorPagesProject.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="xunit.runner.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>Las dependencias clave son:
Microsoft.AspNetCore.Mvc.Testing: proporcionaWebApplicationFactorypara arrancar la aplicación en las pruebasMicrosoft.EntityFrameworkCore.SqlServer: el proveedor de base de datos SQL Server para Entity Framework CoreTestcontainers.MsSql: el módulo de Testcontainers para Microsoft SQL Server
Fábrica de pruebas existente basada en SQLite
El proyecto original incluye una CustomWebApplicationFactory que reemplaza la base de datos de la aplicación con una instancia SQLite en memoria:
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
var dbConnectionDescriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbConnection));
services.Remove(dbConnectionDescriptor);
// Crea una SqliteConnection abierta para que EF no la cierre automáticamente.
services.AddSingleton<DbConnection>(container =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
});
services.AddDbContext<ApplicationDbContext>((container, options) =>
{
var connection = container.GetRequiredService<DbConnection>();
options.UseSqlite(connection);
});
});
builder.UseEnvironment("Development");
}
}Aunque este enfoque funciona, SQLite tiene diferencias de comportamiento con respecto a la base de datos que usarías en producción. En la siguiente sección, la reemplazarás con una instancia de Microsoft SQL Server gestionada por Testcontainers.