Bagikan melalui


Menguji middleware ASP.NET Core

Oleh Chris Ross

Middleware dapat diuji dalam isolasi dengan TestServer. Ini memungkinkan Anda untuk:

  • Membuat instans alur aplikasi yang hanya berisi komponen yang perlu Anda uji.
  • Kirim permintaan kustom untuk memverifikasi perilaku middleware.

Keuntungan:

  • Permintaan dikirim dalam memori daripada diserialisasikan melalui jaringan.
  • Ini menghindari kekhawatiran tambahan, seperti manajemen port dan sertifikat HTTPS.
  • Pengecualian di middleware dapat mengalir langsung kembali ke pengujian panggilan.
  • Dimungkinkan untuk menyesuaikan struktur data server, seperti HttpContext, langsung dalam pengujian.

Menyiapkan TestServer

Dalam proyek pengujian, buat pengujian:

  • Bangun dan mulai host yang menggunakan TestServer.

  • Tambahkan layanan yang diperlukan yang digunakan middleware.

  • Tambahkan referensi paket ke proyek untuk Microsoft.AspNetCore.TestHost paket NuGet.

  • Konfigurasikan alur pemrosesan untuk menggunakan middleware untuk pengujian.

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

Catatan

Untuk panduan tentang menambahkan paket ke aplikasi .NET, lihat artikel di bagian Menginstal dan mengelola paket di Alur kerja konsumsi paket (dokumentasi NuGet). Konfirmasikan versi paket yang benar di NuGet.org.

Mengirim permintaan dengan HttpClient

Kirim permintaan menggunakan 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("/");

    ...
}

Tegaskan hasilnya. Pertama, buat pernyataan kebalikan dari hasil yang Anda harapkan. Eksekusi awal dengan pernyataan positif palsu mengonfirmasi bahwa pengujian gagal ketika middleware berkinerja dengan benar. Jalankan pengujian dan konfirmasikan bahwa pengujian gagal.

Dalam contoh berikut, middleware harus mengembalikan kode status 404 (Tidak Ditemukan) saat titik akhir akar diminta. Lakukan uji coba pertama dengan Assert.NotEqual( ... );, yang seharusnya gagal:

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

Ubah pernyataan untuk menguji middleware dalam kondisi operasi normal. Pengujian akhir menggunakan Assert.Equal( ... );. Jalankan pengujian lagi untuk mengonfirmasi bahwa pengujian lolos.

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

Mengirim permintaan dengan HttpContext

Aplikasi pengujian juga dapat mengirim permintaan menggunakan SendAsync(Action<HttpContext>, CancellationToken). Dalam contoh berikut, beberapa pemeriksaan dilakukan ketika https://example.com/A/Path/?and=query diproses oleh middleware:

[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 mengizinkan konfigurasi HttpContext langsung objek daripada menggunakan HttpClient abstraksi. Gunakan SendAsync untuk memanipulasi struktur yang hanya tersedia di server, seperti HttpContext.Items atau HttpContext.Features.

Seperti contoh sebelumnya yang menguji respons 404 - Tidak Ditemukan , periksa kebalikannya untuk setiap Assert pernyataan dalam pengujian sebelumnya. Pemeriksaan mengonfirmasi bahwa pengujian gagal dengan benar ketika middleware beroperasi secara normal. Setelah Anda mengonfirmasi bahwa pengujian positif palsu berfungsi, atur pernyataan akhir Assert untuk kondisi dan nilai pengujian yang diharapkan. Jalankan lagi untuk mengonfirmasi bahwa pengujian lolos.

Menambahkan rute permintaan

Rute tambahan dapat ditambahkan dengan konfigurasi menggunakan pengujian 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);

Rute tambahan juga dapat ditambahkan menggunakan pendekatan server.SendAsync.

Batasan TestServer

TestServer:

  • Dibuat untuk mereplikasi perilaku server untuk menguji middleware.
  • Tidak mencoba mereplikasi semua HttpClient perilaku.
  • Upaya untuk memberi klien akses ke kontrol sebanyak mungkin atas server, dan dengan visibilitas sebanyak mungkin ke apa yang terjadi di server. Misalnya mungkin melemparkan pengecualian yang biasanya tidak dilemparkan oleh HttpClient untuk langsung mengkomunikasikan status server.
  • Tidak mengatur beberapa header khusus transportasi secara default karena biasanya tidak relevan dengan middleware. Untuk informasi lebih lanjut, lihat bagian berikutnya.
  • Mengabaikan posisi yang Stream diteruskan melalui StreamContent. HttpClient mengirim seluruh aliran dari posisi awal, bahkan saat penempatan diatur. Untuk informasi lebih lanjut, lihat masalah GitHub ini.

Header Panjang Konten dan Pengodean Transfer

TestServer tidak mengatur permintaan terkait transportasi atau header respons seperti Content-Length atau Transfer-Encoding. Aplikasi harus menghindari tergantung pada header ini karena penggunaannya bervariasi menurut klien, skenario, dan protokol. Jika Content-Length dan Transfer-Encoding diperlukan untuk menguji skenario tertentu, skenario tersebut dapat ditentukan dalam pengujian saat menyusun HttpRequestMessage atau HttpContext. Untuk informasi selengkapnya, lihat masalah GitHub berikut ini: