J'essaie d'implémenter l'injection de dépendance dans le test Xunit pour AppService. L'objectif idéal est d'exécuter le programme d'application de démarrage/configuration d'origine, et d'utiliser toute injection de dépendance qui était dans le démarrage, au lieu de réinitialiser à nouveau tous les DI dans mon test, c'est tout l'objectif en question.
Mise à jour: la réponse de Mohsen est proche. Besoin de mettre à jour les erreurs de syntaxe/exigence de couple pour fonctionner.
Pour une raison quelconque, l'application d'origine fonctionne et peut appeler le service App du Département. Cependant, il ne peut pas appeler Xunit. Enfin, Testserver a fonctionné en utilisant le démarrage et la configuration de l'application d'origine. Réception de l'erreur ci-dessous:
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);
}
}
}
Je reçois cette erreur:
Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService
Besoin d'utiliser l'injection de dépendance dans les tests comme une application réelle. L'application originale fait cela. Les réponses ci-dessous ne sont pas actuellement suffisantes, l'une utilise la moquerie qui n'est pas l'objectif actuel, l'autre réponse utilise le contrôleur qui contourne le but de la question.
Remarque: IDepartmentAppService a une dépendance sur IDepartmentRepository qui est également injecté dans la classe Startup et les dépendances Automapper. C'est pourquoi appeler toute la classe de démarrage.
Bonnes ressources:
Utilisez Factory Web Application Factory et ServiceProvider.GetRequiredService ci-dessous, n'hésitez pas à modifier et optimiser la réponse
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);
});
});
}
}
Test d'intégration:
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);
}
}
}
Ressources:
https://docs.Microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
https://fullstackmark.com/post/20/pireless-integration-testing-with-aspnet-core-web-api
Vous mélangez le test unitaire avec le test d'intégration. TestServer
est pour le test d'intégration et si vous souhaitez réutiliser la classe Startup
pour éviter à nouveau les dépendances de registre, vous devez utiliser HttpClient
et effectuer un appel HTTP au contrôleur et l'action qui utilise IDepartmentAppService
.
Si vous voulez faire un test unitaire, vous devez configurer DI et enregistrer toutes les dépendances nécessaires pour tester IDepartmentAppService
.
tilisation de DI via le dispositif de test:
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);
}
}
}
L'utilisation de l'injection de dépendance avec le test unitaire n'est pas une bonne idée et vous devriez l'éviter. soit dit en passant si vous ne voulez pas répéter votre auto pour l'enregistrement des dépendances, vous pouvez encapsuler votre configuration DI dans une autre classe et utiliser cette classe où vous voulez.
tilisation de DI via 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;
}
}
dans la classe Startup
et la méthode ConfigureServices
utilisez simplement la classe 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 vous ne voulez pas utiliser la classe IocConfig
, changez ConfigureServices
dans la classe Startup
:
public IServiceCollection ConfigureServices(IServiceCollection services)
{
.
.
.
return services;
et dans le projet de test, réutilisez la classe IocConfig
ou 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; }
}
et dans la méthode d'essai:
[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);
}
}
Lorsque vous testez. Vous devez utiliser des bibliothèques de simulation ou injecter votre service directement sur le constructeur, c'est-à-dire.
public DBContext context;
public IDepartmentAppService departmentAppService;
/// Inject DepartmentAppService here
public DepartmentAppServiceTest(DepartmentAppService departmentAppService)
{
this.departmentAppService = departmentAppService;
}