Testování middlewaru ASP.NET Core
Autor: Chris Ross
Middleware lze testovat izolovaně s TestServer. Umožňuje:
- Vytvořte instanci kanálu aplikace obsahující pouze komponenty, které potřebujete testovat.
- Odeslání vlastních požadavků za účelem ověření chování middlewaru
Výhody:
- Požadavky se posílají v paměti, nikoli serializovány přes síť.
- Tím se vyhnete dalším obavám, jako je správa portů a certifikáty HTTPS.
- Výjimky v middlewaru můžou proudit přímo zpět do volajícího testu.
- Datové struktury serveru, například HttpContext, je možné přizpůsobit přímo v testu.
Nastavení testovacího serveru
V testovacím projektu vytvořte test:
Sestavte a spusťte hostitele, který používá TestServer.
Přidejte všechny požadované služby, které middleware používá.
Přidejte odkaz na balíček do projektu pro
Microsoft.AspNetCore.TestHost
balíček NuGet.Nakonfigurujte kanál zpracování tak, aby pro test používal middleware.
[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(); ... }
Poznámka:
Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.
Odesílání požadavků pomocí HttpClient
Odeslání žádosti pomocí 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("/");
...
}
Vymažte výsledek. Nejprve proveďte kontrolní výraz, který je opakem očekávaného výsledku. Počáteční spuštění s falešně pozitivním kontrolním výrazem potvrzuje, že test selže, když middleware funguje správně. Spusťte test a ověřte, že test selže.
V následujícím příkladu by middleware měl při vyžádání kořenového koncového bodu vrátit stavový kód 404 (Nenalezený). Proveďte první testovací běh s Assert.NotEqual( ... );
chybou, která by měla selhat:
[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);
}
Změňte kontrolní výraz tak, aby testoval middleware za normálních provozních podmínek. Poslední test používá Assert.Equal( ... );
. Znovu spusťte test, abyste potvrdili, že projde.
[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);
}
Odesílání požadavků pomocí HttpContextu
Testovací aplikace může také odeslat požadavek pomocí SendAsync(Action<HttpContext>, CancellationToken). V následujícím příkladu se provede několik kontrol při https://example.com/A/Path/?and=query
zpracování middlewarem:
[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 umožňuje přímou konfiguraci objektu HttpContext místo použití HttpClient abstrakcí. Slouží SendAsync k manipulaci se strukturami dostupnými pouze na serveru, například HttpContext.Items nebo HttpContext.Features.
Stejně jako v předchozím příkladu, který testoval odpověď 404 – Nenalezena , zkontrolujte opak pro každý Assert
příkaz v předchozím testu. Kontrola potvrzuje, že test selže správně, když middleware funguje normálně. Po potvrzení, že falešně pozitivní test funguje, nastavte konečné Assert
příkazy pro očekávané podmínky a hodnoty testu. Spusťte ho znovu, abyste potvrdili, že test projde.
Přidání tras žádostí
Další trasy je možné přidat pomocí konfigurace pomocí testu 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);
Další trasy lze přidat také pomocí přístupu server.SendAsync
.
Omezení testovacího serveru
TestServer:
- Byl vytvořen pro replikaci chování serveru pro testování middlewaru.
- Nepokouší se replikovat všechna HttpClient chování.
- Snaží se poskytnout klientovi co nejvíce kontroly nad serverem a co nejvíce přehledně zjistit, co se děje na serveru. Může například vyvolat výjimky, které
HttpClient
nejsou obvykle vyvolány, aby bylo možné přímo komunikovat stav serveru. - Nenastavuje ve výchozím nastavení některé hlavičky specifické pro přenos, protože ty obvykle nejsou pro middleware relevantní. Další informace naleznete v následující části.
- Přeskočí
Stream
pozici předanou StreamContent. HttpClient odešle celý datový proud z počáteční pozice, i když je nastaveno umístění. Další informace najdete u tohoto problému na GitHubu.
Hlavičky Content-Length a Transfer-Encoding
TestServer nenastavuje hlavičky požadavku nebo odpovědi související s přenosem, jako je například Content-Length nebo Transfer-Encoding. Aplikace by se měly v závislosti na těchto hlavičkách vyhnout, protože jejich využití se liší podle klienta, scénáře a protokolu. Pokud Content-Length
a Transfer-Encoding
jsou nezbytné k otestování konkrétního scénáře, je možné je zadat v testu při psaní HttpRequestMessage nebo HttpContext. Další informace najdete v následujících problémech s GitHubem: