Testowanie oprogramowania pośredniczącego platformy ASP.NET Core

Autor: Chris Ross

Oprogramowanie pośredniczące można przetestować w izolacji za pomocą polecenia TestServer. Umożliwia to:

  • Utwórz wystąpienie potoku aplikacji zawierającego tylko składniki, które należy przetestować.
  • Wysyłanie niestandardowych żądań w celu zweryfikowania zachowania oprogramowania pośredniczącego.

Zalety:

  • Żądania są wysyłane w pamięci, a nie serializowane za pośrednictwem sieci.
  • Pozwala to uniknąć dodatkowych problemów, takich jak zarządzanie portami i certyfikaty HTTPS.
  • Wyjątki w oprogramowania pośredniczącego mogą przepływać bezpośrednio do testu wywołującego.
  • Istnieje możliwość dostosowania struktur danych serwera, takich jak HttpContext, bezpośrednio w teście.

Konfigurowanie serwera TestServer

W projekcie testowym utwórz test:

  • Skompiluj i uruchom hosta, który używa elementu TestServer.

  • Dodaj wszystkie wymagane usługi używane przez oprogramowanie pośredniczące.

  • Dodaj odwołanie do pakietu nuget do Microsoft.AspNetCore.TestHost projektu.

  • Skonfiguruj potok przetwarzania, aby używać oprogramowania pośredniczącego dla testu.

    [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();
    
        ...
    }
    

Uwaga

Aby uzyskać instrukcje dodawania pakietów do aplikacji .NET, zobacz artykuły w sekcji Instalowanie pakietów i zarządzanie nimi w temacie Przepływ pracy użycia pakietów (dokumentacja programu NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.

Wysyłanie żądań za pomocą obiektu HttpClient

Wyślij żądanie przy użyciu polecenia 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("/");

    ...
}

Potwierdzanie wyniku. Najpierw utwórz asercji przeciwieństwo oczekiwanego wyniku. Początkowy przebieg z potwierdzeniem fałszywie dodatnim potwierdza, że test kończy się niepowodzeniem, gdy oprogramowanie pośredniczące działa prawidłowo. Uruchom test i upewnij się, że test zakończy się niepowodzeniem.

W poniższym przykładzie oprogramowanie pośredniczące powinno zwrócić kod stanu 404 (Nie znaleziono), gdy zażądano głównego punktu końcowego. Wykonaj pierwszy przebieg testu za pomocą Assert.NotEqual( ... );polecenia , który powinien zakończyć się niepowodzeniem:

[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);
}

Zmień asercji, aby przetestować oprogramowanie pośredniczące w normalnych warunkach operacyjnych. W ostatnim teście użyto metody Assert.Equal( ... );. Uruchom ponownie test, aby potwierdzić, że przebiegnie pomyślnie.

[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);
}

Wysyłanie żądań przy użyciu obiektu HttpContext

Aplikacja testowa może również wysłać żądanie przy użyciu polecenia SendAsync(Action<HttpContext>, CancellationToken). W poniższym przykładzie kilka kontroli jest przeprowadzanych podczas https://example.com/A/Path/?and=query przetwarzania przez oprogramowanie pośredniczące:

[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żliwia bezpośrednią konfigurację HttpContext obiektu zamiast używania HttpClient abstrakcji. Umożliwia SendAsync manipulowanie strukturami dostępnymi tylko na serwerze, takimi jak HttpContext.Items lub HttpContext.Features.

Podobnie jak w przypadku wcześniejszego przykładu, który testował odpowiedź 404 — Nie znaleziono , sprawdź odwrotnie dla każdej Assert instrukcji w poprzednim teście. Sprawdzanie potwierdza, że test kończy się niepowodzeniem, gdy oprogramowanie pośredniczące działa normalnie. Po potwierdzeniu, że test fałszywie dodatni działa, ustaw końcowe Assert instrukcje dla oczekiwanych warunków i wartości testu. Uruchom go ponownie, aby potwierdzić, że test przebiegnie pomyślnie.

Dodawanie tras żądań

Dodatkowe trasy można dodać przy użyciu konfiguracji przy użyciu 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);

Dodatkowe trasy można również dodać przy użyciu metody server.SendAsync.

Ograniczenia serwera testowego

Testserver:

  • Utworzono w celu replikowania zachowań serwera w celu przetestowania oprogramowania pośredniczącego.
  • Nie próbuje replikować wszystkich HttpClient zachowań.
  • Próbuje nadać klientowi dostęp do jak największej kontroli nad serwerem i jak najwięcej wglądu w to, co dzieje się na serwerze, jak to możliwe. Na przykład może zgłaszać wyjątki, które nie są zwykle zgłaszane HttpClient , aby bezpośrednio komunikować się ze stanem serwera.
  • Nie ustawia niektórych nagłówków specyficznych dla transportu domyślnie, ponieważ nie są one zwykle istotne dla oprogramowania pośredniczącego. Aby uzyskać więcej informacji, zapoznaj się z następną sekcją.
  • Ignoruje pozycję przekazaną Stream przez StreamContentelement . HttpClient wysyła cały strumień z pozycji początkowej, nawet w przypadku ustawienia pozycjonowania. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub.

Nagłówki Content-Length i Transfer-Encoding

Serwer TestServer nie ustawia nagłówków żądań ani odpowiedzi związanych z transportem, takich jak Content-Length lub Transfer-Encoding. Aplikacje powinny unikać w zależności od tych nagłówków, ponieważ ich użycie różni się w zależności od klienta, scenariusza i protokołu. Jeśli Content-Length konieczne jest Transfer-Encoding przetestowanie określonego scenariusza, można je określić w teście podczas komponowania elementu HttpRequestMessage lub HttpContext. Aby uzyskać więcej informacji, zobacz następujące problemy z usługą GitHub: