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: