c# .net asp.net-core .net-core xunit

c# - Net Core: Ejecutar todas las inyecciones de dependencia en Xunit Test para AppService, Repository, etc.



.net asp.net-core (3)

Cuando estás probando. Debe usar bibliotecas de imitación o inyectar su servicio directamente en contructor, es decir.

public DBContext context; public IDepartmentAppService departmentAppService; /// Inject DepartmentAppService here public DepartmentAppServiceTest(DepartmentAppService departmentAppService) { this.departmentAppService = departmentAppService; }

Estoy tratando de implementar la inyección de dependencia en la prueba Xunit para AppService. El objetivo ideal es ejecutar la configuración / inicio del programa de aplicación original y usar cualquier inyección de dependencia que estaba en el inicio, en lugar de reiniciar todo el DI nuevamente en mi prueba, ese es el objetivo en cuestión.

Actualización: la respuesta de Mohsen está cerca. Es necesario actualizar los errores de sintaxis / requisito de pareja para que funcionen.

Por alguna razón, la aplicación original funciona y puede llamar al Servicio de aplicaciones del departamento. Sin embargo, no puede llamar a Xunit. Finalmente conseguí que Testserver funcionara usando Inicio y Configuración desde la aplicación original. Ahora recibiendo el error a continuación:

Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService namespace Testing.IntegrationTests { public class DepartmentAppServiceTest { public DBContext context; public IDepartmentAppService departmentAppService; public DepartmentAppServiceTest(IDepartmentAppService departmentAppService) { this.departmentAppService = departmentAppService; } [Fact] public async Task Get_DepartmentById_Are_Equal() { var options = new DbContextOptionsBuilder<SharedServicesContext>() .UseInMemoryDatabase(databaseName: "TestDatabase") .Options; context = new DBContext(options); TestServer _server = new TestServer(new WebHostBuilder() .UseContentRoot("C://OriginalApplication") .UseEnvironment("Development") .UseConfiguration(new ConfigurationBuilder() .SetBasePath("C://OriginalApplication") .AddJsonFile("appsettings.json") .Build()).UseStartup<Startup>()); context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" }); context.SaveChanges(); var departmentDto = await departmentAppService.GetDepartmentById(2); Assert.Equal("123", departmentDto.DepartmentCode); } } }

Estoy recibiendo este error:

Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService

Necesita usar la inyección de dependencia en las pruebas como una aplicación real. La aplicación original hace esto. Las respuestas a continuación no son suficientes actualmente, una usa burlas que no es el objetivo actual, otra respuesta usa Controlador que omite el propósito de la pregunta.

Nota: IDepartmentAppService depende de IDepartmentRepository, que también se inyecta en las clases Startup y Automapper. Es por eso que llama a toda la clase de inicio.

Buenos recursos:

cómo probar la aplicación básica asp.net core con inyección de dependencia del constructor

Inyección de dependencias en proyecto Xunit


Está mezclando prueba unitaria con prueba de integración. TestServer es para prueba de integración y si desea reutilizar la clase de Startup para evitar las dependencias de registro nuevamente, debe usar HttpClient y hacer una llamada HTTP al controlador y la acción que usa IDepartmentAppService .

Si desea hacer una prueba unitaria, debe configurar DI y registrar todas las dependencias necesarias para probar IDepartmentAppService .

Usando DI a través del accesorio de prueba:

public class DependencySetupFixture { public DependencySetupFixture() { var serviceCollection = new ServiceCollection(); serviceCollection.AddDbContext<SharedServicesContext>(options => options.UseInMemoryDatabase(databaseName: "TestDatabase")); serviceCollection.AddTransient<IDepartmentRepository, DepartmentRepository>(); serviceCollection.AddTransient<IDepartmentAppService, DepartmentAppService>(); ServiceProvider = serviceCollection.BuildServiceProvider(); } public ServiceProvider ServiceProvider { get; private set; } } public class DepartmentAppServiceTest : IClassFixture<DependencySetupFixture> { private ServiceProvider _serviceProvide; public DepartmentAppServiceTest(DependencySetupFixture fixture) { _serviceProvide = fixture.ServiceProvider; } [Fact] public async Task Get_DepartmentById_Are_Equal() { using(var scope = _serviceProvider.CreateScope()) { // Arrange var context = scope.ServiceProvider.GetServices<SharedServicesContext>(); context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" }); context.SaveChanges(); var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>(); // Act var departmentDto = await departmentAppService.GetDepartmentById(2); // Arrange Assert.Equal("123", departmentDto.DepartmentCode); } } }

Usar inyección de dependencia con prueba unitaria no es una buena idea y debe evitarlo. por cierto, si desea no repetir su auto para registrar dependencias, puede ajustar su configuración de DI en otra clase y usar esa clase en cualquier lugar que desee.

Usando DI a través de Startup.cs:

public class IocConfig { public static IServiceCollection Configure(IServiceCollection services, IConfiguration configuration) { serviceCollection .AddDbContext<SomeContext>(options => options.UseSqlServer(configuration["ConnectionString"])); serviceCollection.AddScoped<IDepartmentRepository, DepartmentRepository>(); serviceCollection.AddScoped<IDepartmentAppService, DepartmentAppService>(); . . . return services; } }

en la clase de Startup y el método ConfigureServices solo use la clase IocConfig :

public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { IocConfig.Configure(services, configuration); services.AddMvc(); . . .

si no desea usar la clase IocConfig , cambie ConfigureServices en la clase Startup :

public IServiceCollection ConfigureServices(IServiceCollection services) { . . . return services;

y en el proyecto de prueba reutilice la clase IocConfig o Startup :

public class DependencySetupFixture { public DependencySetupFixture() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", false, true)); configuration = builder.Build(); var services = new ServiceCollection(); // services = IocConfig.Configure(services, configuration) // or // services = new Startup(configuration).ConfigureServices(services); ServiceProvider = services.BuildServiceProvider(); } public ServiceProvider ServiceProvider { get; private set; } }

y en método de prueba:

[Fact] public async Task Get_DepartmentById_Are_Equal() { using (var scope = _serviceProvider.CreateScope()) { // Arrange var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>(); // Act var departmentDto = await departmentAppService.GetDepartmentById(2); // Arrange Assert.Equal("123", departmentDto.DepartmentCode); } }


Use Custom Web Application Factory y ServiceProvider.ObtengaRequiredService a continuación, no dude en editar y optimizar la respuesta

CustomWebApplicationFactory:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureAppConfiguration((hostingContext, configurationBuilder) => { var type = typeof(TStartup); var path = @"C://OriginalApplication"; configurationBuilder.AddJsonFile($"{path}//appsettings.json", optional: true, reloadOnChange: true); configurationBuilder.AddEnvironmentVariables(); }); // if you want to override Physical database with in-memory database builder.ConfigureServices(services => { var serviceProvider = new ServiceCollection() .AddEntityFrameworkInMemoryDatabase() .BuildServiceProvider(); services.AddDbContext<ApplicationDBContext>(options => { options.UseInMemoryDatabase("DBInMemoryTest"); options.UseInternalServiceProvider(serviceProvider); }); }); } }

Examen de integración:

public class DepartmentAppServiceTest : IClassFixture<CustomWebApplicationFactory<OriginalApplication.Startup>> { public CustomWebApplicationFactory<OriginalApplication.Startup> _factory; public DepartmentAppServiceTest(CustomWebApplicationFactory<OriginalApplication.Startup> factory) { _factory = factory; _factory.CreateClient(); } [Fact] public async Task ValidateDepartmentAppService() { using (var scope = _factory.Server.Host.Services.CreateScope()) { var departmentAppService = scope.ServiceProvider.GetRequiredService<IDepartmentAppService>(); var dbtest = scope.ServiceProvider.GetRequiredService<ApplicationDBContext>(); dbtest.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" }); dbtest.SaveChanges(); var departmentDto = await departmentAppService.GetDepartmentById(2); Assert.Equal("123", departmentDto.DepartmentCode); } } }

Recursos:

https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2

https://fullstackmark.com/post/20/painless-integration-testing-with-aspnet-core-web-api