Razor Pengujian unit halaman di ASP.NET Core

ASP.NET Core mendukung pengujian Razor unit aplikasi Pages. Pengujian lapisan akses data (DAL) dan model halaman membantu memastikan:

  • Bagian dari Razor aplikasi Pages bekerja secara independen dan bersama-sama sebagai unit selama konstruksi aplikasi.
  • Kelas dan metode memiliki cakupan tanggung jawab yang terbatas.
  • Dokumentasi tambahan ada tentang bagaimana aplikasi harus berakibat.
  • Regresi, yang merupakan kesalahan yang dibawa oleh pembaruan pada kode, ditemukan selama pembuatan dan penyebaran otomatis.

Topik ini mengasumsikan bahwa Anda memiliki pemahaman Razor dasar tentang aplikasi Pages dan pengujian unit. Jika Anda tidak terbiasa dengan Razor aplikasi Pages atau konsep pengujian, lihat topik berikut:

Melihat atau mengunduh kode sampel (cara mengunduh)

Proyek sampel terdiri dari dua aplikasi:

App Folder proyek Deskripsi
Aplikasi pesan src/RazorPagesTestSample Memungkinkan pengguna menambahkan pesan, menghapus satu pesan, menghapus semua pesan, dan menganalisis pesan (menemukan jumlah rata-rata kata per pesan).
Menguji aplikasi tests/RazorPagesTestSample.Tests Digunakan untuk menguji unit model halaman DAL dan Indeks aplikasi pesan.

Pengujian dapat dijalankan menggunakan fitur pengujian bawaan IDE, seperti Visual Studio. Jika menggunakan Visual Studio Code atau baris perintah, jalankan perintah berikut pada prompt perintah di folder tests/RazorPagesTestSample.Tests :

dotnet test

Organisasi aplikasi pesan

Aplikasi pesan adalah Razor sistem pesan Pages dengan karakteristik berikut:

  • Halaman Indeks aplikasi (Pages/Index.cshtml dan Pages/Index.cshtml.cs) menyediakan metode UI dan model halaman untuk mengontrol penambahan, penghapusan, dan analisis pesan (temukan jumlah rata-rata kata per pesan).
  • Pesan dijelaskan oleh Message kelas (Data/Message.cs) dengan dua properti: Id (kunci) dan Text (pesan). Properti Text diperlukan dan dibatasi hingga 200 karakter.
  • Pesan disimpan menggunakan database dalam memori Entity Framework†.
  • Aplikasi ini berisi DAL di kelas konteks databasenya, AppDbContext (Data/AppDbContext.cs). Metode DAL ditandai virtual, yang memungkinkan mocking metode untuk digunakan dalam pengujian.
  • Jika database kosong pada startup aplikasi, penyimpanan pesan diinisialisasi dengan tiga pesan. Pesan benih ini juga digunakan dalam pengujian.

† Topik EF, Uji dengan InMemory, menjelaskan cara menggunakan database dalam memori untuk pengujian dengan MSTest. Topik ini menggunakan kerangka kerja pengujian xUnit . Konsep pengujian dan implementasi pengujian di berbagai kerangka kerja pengujian serupa tetapi tidak identik.

Meskipun aplikasi sampel tidak menggunakan pola repositori dan bukan contoh pola Unit kerja (UoW) yang efektif, Razor Pages mendukung pola pengembangan ini. Untuk informasi selengkapnya, lihat Merancang lapisan persistensi infrastruktur dan Logika pengontrol Pengujian di ASP.NET Core (sampel mengimplementasikan pola repositori).

Menguji organisasi aplikasi

Aplikasi pengujian adalah aplikasi konsol di dalam folder tests/RazorPagesTestSample.Tests .

Menguji folder aplikasi Deskripsi
UnitTests
  • DataAccessLayerTest.cs berisi pengujian unit untuk DAL.
  • IndexPageTests.cs berisi pengujian unit untuk model halaman Indeks.
Utilitas Berisi metode yang TestDbContextOptions digunakan untuk membuat opsi konteks database baru untuk setiap pengujian unit DAL sehingga database diatur ulang ke kondisi garis besarnya untuk setiap pengujian.

Kerangka kerja pengujian adalah xUnit. Kerangka kerja tiruan objek adalah Moq.

Pengujian unit lapisan akses data (DAL)

Aplikasi pesan memiliki DAL dengan empat metode yang terkandung dalam AppDbContext kelas (src/RazorPagesTestSample/Data/AppDbContext.cs). Setiap metode memiliki satu atau dua pengujian unit di aplikasi pengujian.

Metode DAL Function
GetMessagesAsync Mendapatkan dari database yang List<Message> diurutkan menurut Text properti .
AddMessageAsync Message Menambahkan ke database.
DeleteAllMessagesAsync Menghapus semua Message entri dari database.
DeleteMessageAsync Menghapus satu Message dari database dengan Id.

Pengujian unit DAL diperlukan DbContextOptions saat membuat baru AppDbContext untuk setiap pengujian. Salah satu pendekatan untuk membuat DbContextOptions untuk setiap pengujian adalah menggunakan DbContextOptionsBuilder:

var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
    .UseInMemoryDatabase("InMemoryDb");

using (var db = new AppDbContext(optionsBuilder.Options))
{
    // Use the db here in the unit test.
}

Masalah dengan pendekatan ini adalah bahwa setiap pengujian menerima database dalam status apa pun yang ditinggalkan oleh pengujian sebelumnya. Ini bisa bermasalah ketika mencoba menulis tes unit atom yang tidak mengganggu satu sama lain. Untuk memaksa AppDbContext penggunaan konteks database baru untuk setiap pengujian, berikan DbContextOptions instans yang didasarkan pada penyedia layanan baru. Aplikasi pengujian menunjukkan cara melakukan ini menggunakan metode TestDbContextOptions kelasnya Utilities (tests/RazorPagesTestSample.Tests/Utilities/Utilities.cs):

public static DbContextOptions<AppDbContext> TestDbContextOptions()
{
    // Create a new service provider to create a new in-memory database.
    var serviceProvider = new ServiceCollection()
        .AddEntityFrameworkInMemoryDatabase()
        .BuildServiceProvider();

    // Create a new options instance using an in-memory database and 
    // IServiceProvider that the context should resolve all of its 
    // services from.
    var builder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb")
        .UseInternalServiceProvider(serviceProvider);

    return builder.Options;
}

DbContextOptions Menggunakan dalam pengujian unit DAL memungkinkan setiap pengujian berjalan secara atomik dengan instans database baru:

using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
{
    // Use the db here in the unit test.
}

Setiap metode pengujian di DataAccessLayerTest kelas (UnitTests/DataAccessLayerTest.cs) mengikuti pola Arrange-Act-Assert serupa:

  1. Arrange: Database dikonfigurasi untuk pengujian dan/atau hasil yang diharapkan ditentukan.
  2. Act: Pengujian dijalankan.
  3. Pernyataan: Pernyataan dibuat untuk menentukan apakah hasil pengujian berhasil.

Misalnya, DeleteMessageAsync metode ini bertanggung jawab untuk menghapus satu pesan yang diidentifikasi oleh Id (src/RazorPagesTestSample/Data/AppDbContext.cs):

public async virtual Task DeleteMessageAsync(int id)
{
    var message = await Messages.FindAsync(id);

    if (message != null)
    {
        Messages.Remove(message);
        await SaveChangesAsync();
    }
}

Ada dua tes untuk metode ini. Satu pengujian memeriksa bahwa metode menghapus pesan saat pesan ada dalam database. Metode lain menguji bahwa database tidak berubah jika pesan Id untuk penghapusan tidak ada. Metode DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound ini ditunjukkan di bawah ini:

[Fact]
public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var seedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(seedMessages);
        await db.SaveChangesAsync();
        var recId = 1;
        var expectedMessages = 
            seedMessages.Where(message => message.Id != recId).ToList();

        // Act
        await db.DeleteMessageAsync(recId);

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Pertama, metode melakukan langkah Susun, di mana persiapan untuk langkah Undang-Undang berlangsung. Pesan seeding diperoleh dan disimpan di seedMessages. Pesan seeding disimpan ke dalam database. Pesan dengan Id dari 1 diatur untuk dihapus. DeleteMessageAsync Ketika metode dijalankan, pesan yang diharapkan harus memiliki semua pesan kecuali untuk pesan dengan Id .1 Variabel expectedMessages mewakili hasil yang diharapkan ini.

// Arrange
var seedMessages = AppDbContext.GetSeedingMessages();
await db.AddRangeAsync(seedMessages);
await db.SaveChangesAsync();
var recId = 1;
var expectedMessages = 
    seedMessages.Where(message => message.Id != recId).ToList();

Metode bertindak: Metode DeleteMessageAsync dijalankan melewati di recId dari 1:

// Act
await db.DeleteMessageAsync(recId);

Akhirnya, metode memperoleh Messages dari konteks dan membandingkannya expectedMessages dengan menegaskan bahwa keduanya sama:

// Assert
var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Untuk membandingkan bahwa keduanya List<Message> sama:

  • Pesan diurutkan oleh Id.
  • Pasangan pesan dibandingkan pada Text properti .

Metode pengujian serupa, DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound memeriksa hasil upaya untuk menghapus pesan yang tidak ada. Dalam hal ini, pesan yang diharapkan dalam database harus sama dengan pesan aktual setelah DeleteMessageAsync metode dijalankan. Seharusnya tidak ada perubahan pada konten database:

[Fact]
public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var expectedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(expectedMessages);
        await db.SaveChangesAsync();
        var recId = 4;

        // Act
        try
        {
            await db.DeleteMessageAsync(recId);
        }
        catch
        {
            // recId doesn't exist
        }

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Pengujian unit metode model halaman

Serangkaian pengujian unit lainnya bertanggung jawab atas pengujian metode model halaman. Di aplikasi pesan, model halaman Indeks ditemukan di IndexModel kelas di src/RazorPagesTestSample/Pages/Index.cshtml.cs.

Metode model halaman Function
OnGetAsync Mendapatkan pesan dari DAL untuk UI menggunakan metode .GetMessagesAsync
OnPostAddMessageAsync Jika ModelState valid, panggilan AddMessageAsync untuk menambahkan pesan ke database.
OnPostDeleteAllMessagesAsync DeleteAllMessagesAsync Panggilan untuk menghapus semua pesan dalam database.
OnPostDeleteMessageAsync DeleteMessageAsync Menjalankan untuk menghapus pesan dengan yang Id ditentukan.
OnPostAnalyzeMessagesAsync Jika satu atau beberapa pesan berada dalam database, menghitung jumlah rata-rata kata per pesan.

Metode model halaman diuji menggunakan tujuh pengujian di IndexPageTests kelas (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs). Pengujian menggunakan pola Arrange-Assert-Act yang sudah dikenal. Pengujian ini berfokus pada:

  • Menentukan apakah metode mengikuti perilaku yang benar ketika ModelState tidak valid.
  • Mengonfirmasi metode menghasilkan yang benar IActionResult.
  • Memeriksa apakah penetapan nilai properti dibuat dengan benar.

Grup pengujian ini sering meniru metode DAL untuk menghasilkan data yang diharapkan untuk langkah Undang-Undang tempat metode model halaman dijalankan. Misalnya, GetMessagesAsync metode ditiru AppDbContext untuk menghasilkan output. Ketika metode model halaman menjalankan metode ini, tiruan mengembalikan hasilnya. Data tidak berasal dari database. Ini menciptakan kondisi pengujian yang dapat diprediksi dan dapat diandalkan untuk menggunakan DAL dalam pengujian model halaman.

Pengujian OnGetAsync_PopulatesThePageModel_WithAListOfMessages menunjukkan bagaimana metode ditiru GetMessagesAsync untuk model halaman:

var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
var expectedMessages = AppDbContext.GetSeedingMessages();
mockAppDbContext.Setup(
    db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
var pageModel = new IndexModel(mockAppDbContext.Object);

OnGetAsync Ketika metode dijalankan dalam langkah UU, metode ini memanggil metode model GetMessagesAsync halaman.

Langkah Undang-Undang pengujian unit (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage metode model OnGetAsync halaman (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

public async Task OnGetAsync()
{
    Messages = await _db.GetMessagesAsync();
}

Metode GetMessagesAsync dalam DAL tidak mengembalikan hasil untuk panggilan metode ini. Versi metode yang ditidakan mengembalikan hasilnya.

Pada langkah tersebut Assert , pesan aktual (actualMessages) ditetapkan dari Messages properti model halaman. Pemeriksaan jenis juga dilakukan saat pesan ditetapkan. Pesan yang diharapkan dan aktual dibandingkan dengan propertinya Text . Pengujian menegaskan bahwa kedua List<Message> instans berisi pesan yang sama.

// Assert
var actualMessages = Assert.IsAssignableFrom<List<Message>>(pageModel.Messages);
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Pengujian lain dalam grup ini membuat objek model halaman yang menyertakan DefaultHttpContext, , ModelStateDictionaryan ActionContext untuk membuat PageContext, ViewDataDictionary, dan PageContext. Ini berguna dalam melakukan tes. Misalnya, aplikasi pesan membuat ModelState kesalahan dengan AddModelError untuk memeriksa apakah valid PageResult dikembalikan saat OnPostAddMessageAsync dijalankan:

[Fact]
public async Task OnPostAddMessageAsync_ReturnsAPageResult_WhenModelStateIsInvalid()
{
    // Arrange
    var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb");
    var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
    var expectedMessages = AppDbContext.GetSeedingMessages();
    mockAppDbContext.Setup(db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
    var httpContext = new DefaultHttpContext();
    var modelState = new ModelStateDictionary();
    var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
    var modelMetadataProvider = new EmptyModelMetadataProvider();
    var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
    var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
    var pageContext = new PageContext(actionContext)
    {
        ViewData = viewData
    };
    var pageModel = new IndexModel(mockAppDbContext.Object)
    {
        PageContext = pageContext,
        TempData = tempData,
        Url = new UrlHelper(actionContext)
    };
    pageModel.ModelState.AddModelError("Message.Text", "The Text field is required.");

    // Act
    var result = await pageModel.OnPostAddMessageAsync();

    // Assert
    Assert.IsType<PageResult>(result);
}

Sumber Daya Tambahan:

ASP.NET Core mendukung pengujian Razor unit aplikasi Pages. Pengujian lapisan akses data (DAL) dan model halaman membantu memastikan:

  • Bagian dari Razor aplikasi Pages bekerja secara independen dan bersama-sama sebagai unit selama konstruksi aplikasi.
  • Kelas dan metode memiliki cakupan tanggung jawab yang terbatas.
  • Dokumentasi tambahan ada tentang bagaimana aplikasi harus berakibat.
  • Regresi, yang merupakan kesalahan yang dibawa oleh pembaruan pada kode, ditemukan selama pembuatan dan penyebaran otomatis.

Topik ini mengasumsikan bahwa Anda memiliki pemahaman Razor dasar tentang aplikasi Pages dan pengujian unit. Jika Anda tidak terbiasa dengan Razor aplikasi Pages atau konsep pengujian, lihat topik berikut:

Melihat atau mengunduh kode sampel (cara mengunduh)

Proyek sampel terdiri dari dua aplikasi:

App Folder proyek Deskripsi
Aplikasi pesan src/RazorPagesTestSample Memungkinkan pengguna menambahkan pesan, menghapus satu pesan, menghapus semua pesan, dan menganalisis pesan (menemukan jumlah rata-rata kata per pesan).
Menguji aplikasi tests/RazorPagesTestSample.Tests Digunakan untuk menguji unit model halaman DAL dan Indeks aplikasi pesan.

Pengujian dapat dijalankan menggunakan fitur pengujian bawaan IDE, seperti Visual Studio. Jika menggunakan Visual Studio Code atau baris perintah, jalankan perintah berikut pada prompt perintah di folder tests/RazorPagesTestSample.Tests :

dotnet test

Organisasi aplikasi pesan

Aplikasi pesan adalah Razor sistem pesan Pages dengan karakteristik berikut:

  • Halaman Indeks aplikasi (Pages/Index.cshtml dan Pages/Index.cshtml.cs) menyediakan metode UI dan model halaman untuk mengontrol penambahan, penghapusan, dan analisis pesan (temukan jumlah rata-rata kata per pesan).
  • Pesan dijelaskan oleh Message kelas (Data/Message.cs) dengan dua properti: Id (kunci) dan Text (pesan). Properti Text diperlukan dan dibatasi hingga 200 karakter.
  • Pesan disimpan menggunakan database dalam memori Entity Framework†.
  • Aplikasi ini berisi DAL di kelas konteks databasenya, AppDbContext (Data/AppDbContext.cs). Metode DAL ditandai virtual, yang memungkinkan mocking metode untuk digunakan dalam pengujian.
  • Jika database kosong pada startup aplikasi, penyimpanan pesan diinisialisasi dengan tiga pesan. Pesan benih ini juga digunakan dalam pengujian.

† Topik EF, Uji dengan InMemory, menjelaskan cara menggunakan database dalam memori untuk pengujian dengan MSTest. Topik ini menggunakan kerangka kerja pengujian xUnit . Konsep pengujian dan implementasi pengujian di berbagai kerangka kerja pengujian serupa tetapi tidak identik.

Meskipun aplikasi sampel tidak menggunakan pola repositori dan bukan contoh pola Unit kerja (UoW) yang efektif, Razor Pages mendukung pola pengembangan ini. Untuk informasi selengkapnya, lihat Merancang lapisan persistensi infrastruktur dan Logika pengontrol Pengujian di ASP.NET Core (sampel mengimplementasikan pola repositori).

Menguji organisasi aplikasi

Aplikasi pengujian adalah aplikasi konsol di dalam folder tests/RazorPagesTestSample.Tests .

Menguji folder aplikasi Deskripsi
UnitTests
  • DataAccessLayerTest.cs berisi pengujian unit untuk DAL.
  • IndexPageTests.cs berisi pengujian unit untuk model halaman Indeks.
Utilitas Berisi metode yang TestDbContextOptions digunakan untuk membuat opsi konteks database baru untuk setiap pengujian unit DAL sehingga database diatur ulang ke kondisi garis besarnya untuk setiap pengujian.

Kerangka kerja pengujian adalah xUnit. Kerangka kerja tiruan objek adalah Moq.

Pengujian unit lapisan akses data (DAL)

Aplikasi pesan memiliki DAL dengan empat metode yang terkandung dalam AppDbContext kelas (src/RazorPagesTestSample/Data/AppDbContext.cs). Setiap metode memiliki satu atau dua pengujian unit di aplikasi pengujian.

Metode DAL Function
GetMessagesAsync Mendapatkan dari database yang List<Message> diurutkan menurut Text properti .
AddMessageAsync Message Menambahkan ke database.
DeleteAllMessagesAsync Menghapus semua Message entri dari database.
DeleteMessageAsync Menghapus satu Message dari database dengan Id.

Pengujian unit DAL diperlukan DbContextOptions saat membuat baru AppDbContext untuk setiap pengujian. Salah satu pendekatan untuk membuat DbContextOptions untuk setiap pengujian adalah menggunakan DbContextOptionsBuilder:

var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
    .UseInMemoryDatabase("InMemoryDb");

using (var db = new AppDbContext(optionsBuilder.Options))
{
    // Use the db here in the unit test.
}

Masalah dengan pendekatan ini adalah bahwa setiap pengujian menerima database dalam status apa pun yang ditinggalkan oleh pengujian sebelumnya. Ini bisa bermasalah ketika mencoba menulis tes unit atom yang tidak mengganggu satu sama lain. Untuk memaksa AppDbContext penggunaan konteks database baru untuk setiap pengujian, berikan DbContextOptions instans yang didasarkan pada penyedia layanan baru. Aplikasi pengujian menunjukkan cara melakukan ini menggunakan metode TestDbContextOptions kelasnya Utilities (tests/RazorPagesTestSample.Tests/Utilities/Utilities.cs):

public static DbContextOptions<AppDbContext> TestDbContextOptions()
{
    // Create a new service provider to create a new in-memory database.
    var serviceProvider = new ServiceCollection()
        .AddEntityFrameworkInMemoryDatabase()
        .BuildServiceProvider();

    // Create a new options instance using an in-memory database and 
    // IServiceProvider that the context should resolve all of its 
    // services from.
    var builder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb")
        .UseInternalServiceProvider(serviceProvider);

    return builder.Options;
}

DbContextOptions Menggunakan dalam pengujian unit DAL memungkinkan setiap pengujian berjalan secara atomik dengan instans database baru:

using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
{
    // Use the db here in the unit test.
}

Setiap metode pengujian di DataAccessLayerTest kelas (UnitTests/DataAccessLayerTest.cs) mengikuti pola Arrange-Act-Assert serupa:

  1. Arrange: Database dikonfigurasi untuk pengujian dan/atau hasil yang diharapkan ditentukan.
  2. Act: Pengujian dijalankan.
  3. Pernyataan: Pernyataan dibuat untuk menentukan apakah hasil pengujian berhasil.

Misalnya, DeleteMessageAsync metode ini bertanggung jawab untuk menghapus satu pesan yang diidentifikasi oleh Id (src/RazorPagesTestSample/Data/AppDbContext.cs):

public async virtual Task DeleteMessageAsync(int id)
{
    var message = await Messages.FindAsync(id);

    if (message != null)
    {
        Messages.Remove(message);
        await SaveChangesAsync();
    }
}

Ada dua tes untuk metode ini. Satu pengujian memeriksa bahwa metode menghapus pesan saat pesan ada dalam database. Metode lain menguji bahwa database tidak berubah jika pesan Id untuk penghapusan tidak ada. Metode DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound ini ditunjukkan di bawah ini:

[Fact]
public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var seedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(seedMessages);
        await db.SaveChangesAsync();
        var recId = 1;
        var expectedMessages = 
            seedMessages.Where(message => message.Id != recId).ToList();

        // Act
        await db.DeleteMessageAsync(recId);

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Pertama, metode melakukan langkah Susun, di mana persiapan untuk langkah Undang-Undang berlangsung. Pesan seeding diperoleh dan disimpan di seedMessages. Pesan seeding disimpan ke dalam database. Pesan dengan Id dari 1 diatur untuk dihapus. DeleteMessageAsync Ketika metode dijalankan, pesan yang diharapkan harus memiliki semua pesan kecuali untuk pesan dengan Id .1 Variabel expectedMessages mewakili hasil yang diharapkan ini.

// Arrange
var seedMessages = AppDbContext.GetSeedingMessages();
await db.AddRangeAsync(seedMessages);
await db.SaveChangesAsync();
var recId = 1;
var expectedMessages = 
    seedMessages.Where(message => message.Id != recId).ToList();

Metode bertindak: Metode DeleteMessageAsync dijalankan melewati di recId dari 1:

// Act
await db.DeleteMessageAsync(recId);

Akhirnya, metode memperoleh Messages dari konteks dan membandingkannya expectedMessages dengan menegaskan bahwa keduanya sama:

// Assert
var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Untuk membandingkan bahwa keduanya List<Message> sama:

  • Pesan diurutkan oleh Id.
  • Pasangan pesan dibandingkan pada Text properti .

Metode pengujian serupa, DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound memeriksa hasil upaya untuk menghapus pesan yang tidak ada. Dalam hal ini, pesan yang diharapkan dalam database harus sama dengan pesan aktual setelah DeleteMessageAsync metode dijalankan. Seharusnya tidak ada perubahan pada konten database:

[Fact]
public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound()
{
    using (var db = new AppDbContext(Utilities.TestDbContextOptions()))
    {
        // Arrange
        var expectedMessages = AppDbContext.GetSeedingMessages();
        await db.AddRangeAsync(expectedMessages);
        await db.SaveChangesAsync();
        var recId = 4;

        // Act
        await db.DeleteMessageAsync(recId);

        // Assert
        var actualMessages = await db.Messages.AsNoTracking().ToListAsync();
        Assert.Equal(
            expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
            actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
    }
}

Pengujian unit metode model halaman

Serangkaian pengujian unit lainnya bertanggung jawab atas pengujian metode model halaman. Di aplikasi pesan, model halaman Indeks ditemukan di IndexModel kelas di src/RazorPagesTestSample/Pages/Index.cshtml.cs.

Metode model halaman Function
OnGetAsync Mendapatkan pesan dari DAL untuk UI menggunakan metode .GetMessagesAsync
OnPostAddMessageAsync Jika ModelState valid, panggilan AddMessageAsync untuk menambahkan pesan ke database.
OnPostDeleteAllMessagesAsync DeleteAllMessagesAsync Panggilan untuk menghapus semua pesan dalam database.
OnPostDeleteMessageAsync DeleteMessageAsync Menjalankan untuk menghapus pesan dengan yang Id ditentukan.
OnPostAnalyzeMessagesAsync Jika satu atau beberapa pesan berada dalam database, menghitung jumlah rata-rata kata per pesan.

Metode model halaman diuji menggunakan tujuh pengujian di IndexPageTests kelas (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs). Pengujian menggunakan pola Arrange-Assert-Act yang sudah dikenal. Pengujian ini berfokus pada:

  • Menentukan apakah metode mengikuti perilaku yang benar ketika ModelState tidak valid.
  • Mengonfirmasi metode menghasilkan yang benar IActionResult.
  • Memeriksa apakah penetapan nilai properti dibuat dengan benar.

Grup pengujian ini sering meniru metode DAL untuk menghasilkan data yang diharapkan untuk langkah Undang-Undang tempat metode model halaman dijalankan. Misalnya, GetMessagesAsync metode ditiru AppDbContext untuk menghasilkan output. Ketika metode model halaman menjalankan metode ini, tiruan mengembalikan hasilnya. Data tidak berasal dari database. Ini menciptakan kondisi pengujian yang dapat diprediksi dan dapat diandalkan untuk menggunakan DAL dalam pengujian model halaman.

Pengujian OnGetAsync_PopulatesThePageModel_WithAListOfMessages menunjukkan bagaimana metode ditiru GetMessagesAsync untuk model halaman:

var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
var expectedMessages = AppDbContext.GetSeedingMessages();
mockAppDbContext.Setup(
    db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
var pageModel = new IndexModel(mockAppDbContext.Object);

OnGetAsync Ketika metode dijalankan dalam langkah UU, metode ini memanggil metode model GetMessagesAsync halaman.

Langkah Undang-Undang pengujian unit (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage metode model OnGetAsync halaman (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

public async Task OnGetAsync()
{
    Messages = await _db.GetMessagesAsync();
}

Metode GetMessagesAsync dalam DAL tidak mengembalikan hasil untuk panggilan metode ini. Versi metode yang ditidakan mengembalikan hasilnya.

Pada langkah tersebut Assert , pesan aktual (actualMessages) ditetapkan dari Messages properti model halaman. Pemeriksaan jenis juga dilakukan saat pesan ditetapkan. Pesan yang diharapkan dan aktual dibandingkan dengan propertinya Text . Pengujian menegaskan bahwa kedua List<Message> instans berisi pesan yang sama.

// Assert
var actualMessages = Assert.IsAssignableFrom<List<Message>>(pageModel.Messages);
Assert.Equal(
    expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), 
    actualMessages.OrderBy(m => m.Id).Select(m => m.Text));

Pengujian lain dalam grup ini membuat objek model halaman yang menyertakan DefaultHttpContext, , ModelStateDictionaryan ActionContext untuk membuat PageContext, ViewDataDictionary, dan PageContext. Ini berguna dalam melakukan tes. Misalnya, aplikasi pesan membuat ModelState kesalahan dengan AddModelError untuk memeriksa apakah valid PageResult dikembalikan saat OnPostAddMessageAsync dijalankan:

[Fact]
public async Task OnPostAddMessageAsync_ReturnsAPageResult_WhenModelStateIsInvalid()
{
    // Arrange
    var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
        .UseInMemoryDatabase("InMemoryDb");
    var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options);
    var expectedMessages = AppDbContext.GetSeedingMessages();
    mockAppDbContext.Setup(db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages));
    var httpContext = new DefaultHttpContext();
    var modelState = new ModelStateDictionary();
    var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
    var modelMetadataProvider = new EmptyModelMetadataProvider();
    var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
    var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
    var pageContext = new PageContext(actionContext)
    {
        ViewData = viewData
    };
    var pageModel = new IndexModel(mockAppDbContext.Object)
    {
        PageContext = pageContext,
        TempData = tempData,
        Url = new UrlHelper(actionContext)
    };
    pageModel.ModelState.AddModelError("Message.Text", "The Text field is required.");

    // Act
    var result = await pageModel.OnPostAddMessageAsync();

    // Assert
    Assert.IsType<PageResult>(result);
}

Sumber Daya Tambahan: