J'ai ce qui suit test d'intégration ASP.NET Core en utilisant un WebApplicationFactory
personnalisé
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public CustomWebApplicationFactory()
{
this.ClientOptions.AllowAutoRedirect = false;
this.ClientOptions.BaseAddress = new Uri("https://localhost");
}
public ApplicationOptions ApplicationOptions { get; private set; }
public Mock<IClockService> ClockServiceMock { get; private set; }
public void VerifyAllMocks() => Mock.VerifyAll(this.ClockServiceMock);
protected override TestServer CreateServer(IWebHostBuilder builder)
{
this.ClockServiceMock = new Mock<IClockService>(MockBehavior.Strict);
builder
.UseEnvironment("Testing")
.ConfigureTestServices(
services =>
{
services.AddSingleton(this.ClockServiceMock.Object);
});
var testServer = base.CreateServer(builder);
using (var serviceScope = testServer.Host.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
this.ApplicationOptions = serviceProvider.GetRequiredService<IOptions<ApplicationOptions>>().Value;
}
return testServer;
}
}
qui semble fonctionner mais le problème est que la méthode ConfigureTestServices
n'est jamais appelée, donc ma maquette n'est jamais enregistrée avec le conteneur IoC. Vous pouvez trouver le code source complet ici .
public class FooControllerTest : IClassFixture<CustomWebApplicationFactory<Startup>>, IDisposable
{
private readonly HttpClient client;
private readonly CustomWebApplicationFactory<Startup> factory;
private readonly Mock<IClockService> clockServiceMock;
public FooControllerTest(CustomWebApplicationFactory<Startup> factory)
{
this.factory = factory;
this.client = factory.CreateClient();
this.clockServiceMock = this.factory.ClockServiceMock;
}
[Fact]
public async Task Delete_FooFound_Returns204NoContent()
{
this.clockServiceMock.SetupGet(x => x.UtcNow).ReturnsAsync(new DateTimeOffset.UtcNow);
var response = await this.client.DeleteAsync("/foo/1");
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
}
public void Dispose() => this.factory.VerifyAllMocks();
}
Vous devez créer un faux démarrage:
public class FakeStartup : Startup
{
public FakeStartup(IConfiguration configuration)
: base(configuration)
{
}
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
// Your fake go here
//services.AddScoped<IService, FakeService>();
}
}
Ensuite, utilisez-le avec IClassFixture<CustomWebApplicationFactory<FakeStartup>>
.
Assurez-vous de créer votre méthode virtuelle ConfigureServices
d'origine.
La meilleure façon de gérer cela est de factoriser les parties de votre Startup
qui devront être remplacées pendant le test. Par exemple, au lieu d'appeler services.AddDbContext<MyContext>(...);
directement dans ConfigureServices
, créez une méthode privée virtuelle comme:
private virtual void ConfigureDatabase(IServiceCollection services)
{
services.AddDbContext<MyContext>(...);
}
Ensuite, dans votre projet de test, créez une classe comme TestStartup
qui dérive de la classe Startup
de votre SUT. Ensuite, vous pouvez remplacer ces méthodes virtuelles pour les sous-traiter dans vos services de test, vos simulations, etc.
Enfin, faites quelque chose comme:
builder
.UseEnvironment("Testing")
.UseStartup<TestStartup>();