作者:Chris Ross
中介軟體可以利用 TestServer 單獨測試。 這可讓您:
- 實例化一個只包含你需要測試元件的應用程式流程。
- 發送自訂請求來驗證中介軟體的行為。
優點:
- 請求是以記憶體方式傳送,而非透過網路序列化。
- 這避免了額外的問題,例如埠管理和 HTTPS 憑證。
- 中介軟體中的例外可以直接回溯到呼叫測試。
- 測試中可以直接自訂伺服器資料結構,例如 HttpContext。
設定測試伺服器
在測試專案中,建立一個測試:
使用 TestServer 建立並啟動主機。
新增中介軟體所需的任何服務。
為專案新增
Microsoft.AspNetCore.TestHostNuGet 套件的套件參考。設定處理管線使用中介軟體進行測試。
[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(); ... }
使用 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 問題。
內容長度與 Transfer-Encoding 標頭
TestServer 不會 設定與傳輸相關的請求或回應標頭,例如 內容長度 或 傳輸編碼。 應用程式應避免依賴這些標頭,因為它們的使用會因客戶端、情境和協定而異。 若 Content-Length 和 Transfer-Encoding 是測試特定情境所必需,則可在撰寫HttpRequestMessage或HttpContext時於測試中指定。 欲了解更多資訊,請參閱以下 GitHub 議題: