Nguon: Microsoft Learn · .NET 8.0

Kiểm thử ASP.NET Core Middleware (Phần mềm trung gian)

Nguồn: Test ASP.NET Core middleware

Bởi Chris Ross

Middleware có thể được kiểm thử độc lập với TestServer. Nó cho phép bạn:

Ưu điểm:

Thiết lập TestServer

Trong project test, tạo một test:

```csharp [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();

... } ```

Gửi request với HttpClient

Gửi một request bằng HttpClient:

csharp
[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("/");

    ...
}

Kiểm tra kết quả. Trước tiên, thực hiện một assertion (khẳng định) ngược lại với kết quả mà bạn mong đợi. Lần chạy ban đầu với assertion false positive xác nhận rằng test thất bại khi middleware hoạt động đúng. Chạy test và xác nhận rằng test thất bại.

Trong ví dụ sau, middleware phải trả về status code (mã trạng thái) 404 (Not Found) khi root endpoint (endpoint gốc) được yêu cầu. Thực hiện lần chạy test đầu tiên với Assert.NotEqual( ... );, điều này sẽ thất bại:

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

Thay đổi assertion để kiểm thử middleware trong điều kiện hoạt động bình thường. Test cuối cùng dùng Assert.Equal( ... );. Chạy lại test để xác nhận nó pass (thành công).

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

Gửi request với HttpContext

Ứng dụng test cũng có thể gửi request bằng SendAsync(Action<HttpContext>, CancellationToken). Trong ví dụ sau, một số kiểm tra được thực hiện khi https://example.com/A/Path/?and=query được middleware xử lý:

csharp
[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 cho phép cấu hình trực tiếp đối tượng HttpContext thay vì sử dụng abstraction (trừu tượng hóa) HttpClient. Dùng SendAsync để thao tác các cấu trúc chỉ có sẵn trên server, chẳng hạn như HttpContext.Items hoặc HttpContext.Features.

Giống với ví dụ trước kiểm tra response 404 - Not Found, hãy kiểm tra ngược lại cho mỗi câu lệnh Assert trong test trên. Việc kiểm tra xác nhận rằng test thất bại đúng cách khi middleware hoạt động bình thường. Sau khi xác nhận false positive test hoạt động, đặt các câu lệnh Assert cuối cùng cho các điều kiện và giá trị mong đợi của test. Chạy lại để xác nhận test pass.

Thêm route (tuyến đường) cho request

Các route bổ sung có thể được thêm bằng cách cấu hình sử dụng HttpClient test:

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

Các route bổ sung cũng có thể được thêm bằng cách dùng server.SendAsync.

Giới hạn của TestServer

TestServer:

Headers Content-Length và Transfer-Encoding

TestServer không đặt các request hoặc response headers liên quan đến transport như Content-Length hay Transfer-Encoding. Ứng dụng nên tránh phụ thuộc vào các headers này vì việc sử dụng của chúng thay đổi theo client, scenario (kịch bản) và protocol (giao thức). Nếu Content-LengthTransfer-Encoding cần thiết để kiểm thử một scenario cụ thể, chúng có thể được chỉ định trong test khi soạn HttpRequestMessage hoặc HttpContext.