Nota
L'accesso a questa pagina richiede l'autorizzazione. Puoi provare ad accedere o a cambiare directory.
L'accesso a questa pagina richiede l'autorizzazione. Puoi provare a cambiare directory.
Di Chris Ross
Il middleware può essere testato in isolamento con TestServer. Consente di:
- Istanziare una pipeline dell'app contenente solo i componenti da testare.
- Inviare richieste personalizzate per verificare il comportamento del middleware.
Vantaggi:
- Le richieste vengono inviate in memoria anziché serializzate in rete.
- In questo modo si evitano problemi aggiuntivi, ad esempio la gestione delle porte e i certificati HTTPS.
- Le eccezioni nel middleware possono tornare direttamente al test chiamante.
- È possibile personalizzare le strutture dei dati del server, ad esempio HttpContext, direttamente nel test.
Configurare TestServer
Nel progetto di test creare un test:
Compilare e avviare un host che usa TestServer.
Aggiungere tutti i servizi necessari usati dal middleware.
Aggiungere al progetto un riferimento al pacchetto per il
Microsoft.AspNetCore.TestHostpacchetto NuGet.Configurare la pipeline di elaborazione per usare il middleware per il test.
[Fact] public async Task MiddlewareTest_ReturnsNotFoundForRequest() { using var host = await new HostBuilder() .ConfigureWebHost(webBuilder => { webBuilder .UseTestServer() .ConfigureServices(services => { services.AddMyServices(); }) .Configure(app => { app.UseMiddleware<MyMiddleware>(); }); }) .StartAsync(); ... }
Annotazioni
Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Verificare le versioni corrette dei pacchetti in NuGet.org.
Inviare richieste con HttpClient
Inviare una richiesta usando HttpClient:
[Fact]
public async Task MiddlewareTest_ReturnsNotFoundForRequest()
{
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddMyServices();
})
.Configure(app =>
{
app.UseMiddleware<MyMiddleware>();
});
})
.StartAsync();
var response = await host.GetTestClient().GetAsync("/");
...
}
Confermare il risultato. Prima di tutto, creare un'asserzione l'opposto del risultato previsto. Un'esecuzione iniziale con un'asserzione di falso positivo conferma che il test viene respinto quando il middleware funziona correttamente. Eseguire il test e verificare che il test fallisca.
Nell'esempio seguente, il middleware deve restituire un codice di stato 404 (Non trovato) quando viene richiesto l'endpoint radice. Eseguire il primo test con Assert.NotEqual( ... );, che dovrebbe avere esito negativo:
[Fact]
public async Task MiddlewareTest_ReturnsNotFoundForRequest()
{
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddMyServices();
})
.Configure(app =>
{
app.UseMiddleware<MyMiddleware>();
});
})
.StartAsync();
var response = await host.GetTestClient().GetAsync("/");
Assert.NotEqual(HttpStatusCode.NotFound, response.StatusCode);
}
Modificare l'asserzione per testare il middleware in condizioni operative normali. Il test finale usa Assert.Equal( ... );. Eseguire di nuovo il test per verificare che venga superato.
[Fact]
public async Task MiddlewareTest_ReturnsNotFoundForRequest()
{
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddMyServices();
})
.Configure(app =>
{
app.UseMiddleware<MyMiddleware>();
});
})
.StartAsync();
var response = await host.GetTestClient().GetAsync("/");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
Inviare richieste con HttpContext
Un'app di test può anche inviare una richiesta usando SendAsync(Action<HttpContext>, CancellationToken). Nell'esempio seguente vengono eseguiti diversi controlli quando https://example.com/A/Path/?and=query viene elaborato dal middleware:
[Fact]
public async Task TestMiddleware_ExpectedResponse()
{
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddMyServices();
})
.Configure(app =>
{
app.UseMiddleware<MyMiddleware>();
});
})
.StartAsync();
var server = host.GetTestServer();
server.BaseAddress = new Uri("https://example.com/A/Path/");
var context = await server.SendAsync(c =>
{
c.Request.Method = HttpMethods.Post;
c.Request.Path = "/and/file.txt";
c.Request.QueryString = new QueryString("?and=query");
});
Assert.True(context.RequestAborted.CanBeCanceled);
Assert.Equal(HttpProtocol.Http11, context.Request.Protocol);
Assert.Equal("POST", context.Request.Method);
Assert.Equal("https", context.Request.Scheme);
Assert.Equal("example.com", context.Request.Host.Value);
Assert.Equal("/A/Path", context.Request.PathBase.Value);
Assert.Equal("/and/file.txt", context.Request.Path.Value);
Assert.Equal("?and=query", context.Request.QueryString.Value);
Assert.NotNull(context.Request.Body);
Assert.NotNull(context.Request.Headers);
Assert.NotNull(context.Response.Headers);
Assert.NotNull(context.Response.Body);
Assert.Equal(404, context.Response.StatusCode);
Assert.Null(context.Features.Get<IHttpResponseFeature>().ReasonPhrase);
}
SendAsync consente la configurazione diretta di un HttpContext oggetto anziché l'uso delle HttpClient astrazioni. Usare SendAsync per modificare le strutture disponibili solo nel server, ad esempio HttpContext.Items o HttpContext.Features.
Come nell'esempio precedente che verificava una risposta 404 - Non trovato, verificare il contrario di quanto fatto per ogni Assert istruzione nel test descritto sopra. Il controllo conferma che il test non riesce correttamente quando il middleware funziona normalmente. Dopo aver confermato che il test falso positivo funziona, imposta le istruzioni finali Assert per le condizioni e i valori previsti del test. Eseguirlo di nuovo per verificare che il test venga superato.
Aggiungere percorsi di richiesta
È possibile aggiungere route aggiuntive tramite la configurazione usando il test HttpClient:
[Fact]
public async Task TestWithEndpoint_ExpectedResponse ()
{
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddRouting();
})
.Configure(app =>
{
app.UseRouting();
app.UseMiddleware<MyMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/hello", () =>
TypedResults.Text("Hello Tests"));
});
});
})
.StartAsync();
var client = host.GetTestClient();
var response = await client.GetAsync("/hello");
Assert.True(response.IsSuccessStatusCode);
var responseBody = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello Tests", responseBody);
È anche possibile aggiungere route aggiuntive usando l'approccio server.SendAsync.
Limitazioni di TestServer
TestServer:
- È stato creato per replicare i comportamenti del server per testare il middleware.
- Non tenta di replicare tutti i HttpClient comportamenti.
- Tenta di fornire al client il massimo controllo possibile sul server e la massima trasparenza su quanto accade sul server. Ad esempio, può generare eccezioni non generate normalmente da
HttpClientper comunicare direttamente lo stato del server. - Non imposta per impostazione predefinita alcune intestazioni specifiche del trasporto, poiché solitamente non sono rilevanti per il middleware. Per altre informazioni, vedere la sezione successiva.
- Ignora la
Streamposizione passata tramite StreamContent. HttpClient invia l'intero flusso dalla posizione iniziale, anche quando viene impostato il posizionamento. Per altre informazioni, vedere questo problema in GitHub.
Intestazioni Content-Length e Transfer-Encoding
TestServer non imposta intestazioni di richiesta o risposta correlate al trasporto, ad esempio Content-Length o Transfer-Encoding. Le applicazioni devono evitare di dipendere da queste intestazioni perché l'utilizzo varia in base a client, scenario e protocollo. Se Content-Length e Transfer-Encoding sono necessari per testare uno scenario specifico, possono essere specificati nel test durante la composizione di HttpRequestMessage o HttpContext. Per altre informazioni, vedere i problemi di GitHub seguenti: