測試 ASP.NET Core 中介軟體
作者:Chris Ross
中介軟體可使用 TestServer 以隔離方式進行測試。 這可讓您:
- 具現化只包含您需測試元件的應用程式管線。
- 傳送自訂要求以驗證中介軟體行為。
優點:
- 要求會在記憶體內部傳送,而不是透過網路序列化。
- 這可避免其他疑慮,例如連結埠管理和 HTTPS 憑證。
- 中介軟體中的例外狀況可直接流回呼叫測試。
- 您可以直接在測試中自訂伺服器資料結構,例如 HttpContext。
設定 TestServer
在測試專案中,建立測試:
建置並啟動使用 TestServer 的主機。
新增中介軟體使用的任何必要服務。
為
Microsoft.AspNetCore.TestHost
NuGet 套件的專案新增套件參考。設定處理管線以使用中介軟體進行測試。
[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(); ... }
注意
如需將套件新增至 .NET 應用程式的指引,請參閱在套件取用工作流程 (NuGet 文件) 的安裝及管理套件底下的文章。 在 NuGet.org 確認正確的套件版本。
使用 HttpClient 傳送要求
使用 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("/");
...
}
判斷提示結果。 首先,做出與您期望的結果相反的判斷提示。 具有誤判為真判斷提示的初始執行會確認當中介軟體正確執行時,測試失敗。 執行測試並確認測試失敗。
在下列範例中,當要求根端點時,中介軟體應該傳回 404 狀態碼 (找不到)。 使用 Assert.NotEqual( ... );
進行第一個測試回合,這應該會失敗:
[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);
}
變更判斷提示,以在正常作業條件下測試中介軟體。 最終測試會使用 Assert.Equal( ... );
。 再次執行測試以確認通過測試。
[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);
}
使用 HttpContext 傳送要求
測試應用程式也可以使用 SendAsync(Action<HttpContext>, CancellationToken) 傳送要求。 在下列範例中,由中介軟體處理 https://example.com/A/Path/?and=query
時,會進行數項檢查:
[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 允許直接設定 HttpContext 物件,而不使用 HttpClient 抽象概念。 使用 SendAsync 來操作僅在伺服器上可用的結構,例如 HttpCoNtext.Items 或 HttpCoNtext.Features。
如同先前針對「404 - 找不到」回應測試的範例,請檢查上述測試中每個 Assert
陳述式的相反項目。 此檢查可確認當中間件正常運作時,測試正確失敗。 確認誤判為真測試可運作之後,請針對測試的預期條件和值設定最終 Assert
陳述式。 再次執行,以確認通過測試。
新增要求路由
您可以使用測試 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);
您也可以使用 方法 server.SendAsync
新增其他路由。
TestServer 限制
TestServer:
- 已建立來複寫伺服器行為,以測試中介軟體。
- 請勿嘗試複寫所有的 HttpClient 行為。
- 嘗試讓用戶端得以盡可能控制伺服器,而且盡可能得知伺服器上發生的情況。 例如,其可能擲回
HttpClient
通常不會擲回的例外狀況,以便直接傳達伺服器狀態。 - 預設不要設定某些傳輸特定標頭,因為這些標頭通常與中介軟體無關。 如需詳細資訊,請參閱一節。
- 忽略透過 StreamContent 傳遞的
Stream
位置。 HttpClient 會從開始位置傳送整個串流,即使已設定定位也一樣。 如需詳細資訊,請參閱這個 GitHub 問題。
Content-Length 和 Transfer-Encoding 標頭
TestServer 不會設定傳輸相關要求或回應標頭,例如 Content-Length 或 Transfer-Encoding。 應用程式應避免取決於這些標頭,因為其使用方式因用戶端、案例和通訊協定而異。 如果需要 Content-Length
和 Transfer-Encoding
來測試特定案例,可在撰寫 HttpRequestMessage 或 HttpContext 時,在測試中加以指定。 如需詳細資訊,請參閱下列 GitHub 問題: