Testovací ASP.NET Core middleware
Chris Neschůdně
Middleware je možné testovat izolovaně pomocí TestServer . Umožňuje:
- Vytvořte instanci kanálu aplikace obsahujícího jenom komponenty, které potřebujete otestovat.
- Odesílání vlastních požadavků za účelem ověření chování middlewaru
Výhody:
- Požadavky jsou odesílány v paměti, nikoli serializován přes síť.
- Tím se vyhnete dalším obavám, jako je správa portů a certifikáty HTTPS.
- Výjimky v middlewaru mohou proudit přímo zpět do volajícího testu.
- Datové struktury serveru, jako je , je možné přizpůsobit HttpContext přímo v testu.
Nastavení testovacího serveru
V projektu testů vytvořte test:
Sestavte a spusťte hostitele, který používá TestServer .
Přidejte všechny požadované služby, které middleware používá.
Do projektu přidejte NuGet Microsoft.AspNetCore.TestHost:
<ItemGroup> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.*" /> </ItemGroup>Nakonfigurujte kanál zpracování pro použití middlewaru pro 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();
...
}
Odesílání požadavků pomocí HttpClient
Odešlete požadavek pomocí HttpClient příkazu :
[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("/");
...
}
Prohlédněte si výsledek. Nejprve proveďte kontrolní výraz jako opak výsledku, který očekáváte. Při počátečním spuštění s falešně pozitivním kontrolním výrazem se potvrdí, ž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 ( Nenašl se). Proveďte první testovací běh pomocí Assert.NotEqual( ... ); , který by měl 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ěnou kontrolního výrazu otestujte middleware za normálních provozních podmínek. Poslední test používá Assert.Equal( ... ); . Spusťte test znovu, abyste potvrdili, že byl úspěšně.
[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ů s httpcontext
Testovací aplikace může také odeslat požadavek pomocí metody SendAsync(Action <HttpContext> , CancellationToken). V následujícím příkladu je při zpracování middlewarem provedeno https://example.com/A/Path/?and=query několik kontrol:
[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 HttpContext objektu 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 u předchozího příkladu, který testoval odpověď 404 – Nenalehlé, zkontrolujte pro každý příkaz v Assert předchozím testu opak. Při normálním provozu middlewaru kontrola potvrdí, že test selže správně. Jakmile potvrdíte, že falešně pozitivní test funguje, nastavte konečné příkazy pro očekávané Assert podmínky a hodnoty testu. Spusťte ho znovu, abyste potvrdili, že test byl úspěšně.
Omezení testserveru
TestServer:
- Byl vytvořen pro replikaci chování serveru pro testování middlewaru.
- Nepokouší se replikovat všechna HttpClient chování.
- Pokusí se dát klientovi přístup k co možná nejvíce kontrolu nad serverem a s co možná nejvíce vitelnějšími informace o tom, co se na serveru děje. Může například vyvolat výjimky, které obvykle nevyvolané službou ,
HttpClientaby bylo možné přímo komunikovat stav serveru. - Ve výchozím nastavení nenastaví některé hlavičky specifické pro přenos, protože obvykle nejsou relevantní pro middleware. Další informace naleznete v následujícím oddílu.
Hlavičky Content-Length a Transfer-Encoding
TestServer nenastaví hlavičky požadavku nebo odpovědi související s přenosem, jako je Content-Length nebo Transfer-Encoding. Aplikace by se měly v závislosti na těchto hlavičkách vyhnout, protože jejich použití se liší podle klienta, scénáře a protokolu. Pokud Content-Length jsou a nezbytné k Transfer-Encoding otestování konkrétního scénáře, je možné je zadat v testu při vytváření HttpRequestMessage nebo HttpContext . Další informace najdete v následujících GitHub potíží: