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 , HttpClient aby 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íží: