Razor ASP.NET Core'da sayfalar birim testleri

ASP.NET Core, Pages uygulamalarının birim testlerini Razor destekler. Veri erişim katmanı (DAL) ve sayfa modellerinin testleri şunların sağlanmasına yardımcı olur:

  • Sayfalar uygulamasının Razor bölümleri, uygulama oluşturma sırasında bağımsız olarak ve bir birim olarak birlikte çalışır.
  • Sınıflar ve yöntemler sınırlı sorumluluk kapsamlarına sahiptir.
  • Uygulamanın nasıl davranması gerektiğine ilişkin ek belgeler vardır.
  • Kod güncelleştirmelerinin getirdiği hatalar olan regresyonlar, otomatik derleme ve dağıtım sırasında bulunur.

Bu konu başlığında Sayfalar uygulamaları ve birim testleri hakkında Razor temel bilgilere sahip olduğunuz varsayılır. Sayfalar uygulamaları veya test kavramları hakkında Razor bilginiz yoksa aşağıdaki konulara bakın:

Örnek kodu görüntüleme veya indirme (indirme)

Örnek proje iki uygulamadan oluşur:

App Proje klasörü Tanım
İleti uygulaması src/RazorPagesTestSample Kullanıcının ileti eklemesine, bir iletiyi silmesine, tüm iletileri silmesine ve iletileri analiz etmesine izin verir (ileti başına ortalama sözcük sayısını bulur).
Test uygulaması tests/RazorPagesTestSample.Tests İleti uygulamasının DAL ve Dizin sayfası modelini birim testi için kullanılır.

Testler, Visual Studio gibi bir IDE'nin yerleşik test özellikleri kullanılarak çalıştırılabilir. Visual Studio Code veya komut satırı kullanıyorsanız, tests/RazorPagesTestSample.Tests klasöründeki bir komut isteminde aşağıdaki komutu yürütebilirsiniz:

dotnet test

İleti uygulaması kuruluşu

İleti uygulaması, aşağıdaki özelliklere sahip bir Razor Sayfalar ileti sistemidir:

  • Uygulamanın (Pages/Index.cshtml ve Pages/Index.cshtml.cs) Dizin sayfası, iletilerin eklenmesini, silinmesini ve analizini denetlemek için bir kullanıcı arabirimi ve sayfa modeli yöntemleri sağlar (ileti başına ortalama sözcük sayısını bulur).
  • bir ileti sınıfıData/Message.cs () tarafından Message iki özelliğe sahip olarak açıklanır: Id (anahtar) ve Text (ileti). Text özelliği gereklidir ve 200 karakterle sınırlıdır.
  • İletiler Entity Framework'ün bellek içi veritabanı† kullanılarak depolanır.
  • Uygulama, veritabanı bağlam sınıfında AppDbContextData/AppDbContext.cs() bir DAL içerir. DAL yöntemleri, testlerde kullanılmak üzere yöntemlerin sahtesini oluşturmanızı sağlayan olarak işaretlenir virtual.
  • Uygulama başlangıcında veritabanı boşsa, ileti deposu üç iletiyle başlatılır. Bu dağıtılmış iletiler testlerde de kullanılır.

†InMemory ile Test et ef konusu, MSTest ile testler için bellek içi veritabanının nasıl kullanılacağını açıklar. Bu konu, xUnit test çerçevesini kullanır. Farklı test çerçevelerindeki test kavramları ve test uygulamaları benzerdir ancak aynı değildir.

Örnek uygulama depo desenini kullanmasa ve çalışma birimi (UoW) deseninin etkili bir örneği olmasa da, Razor Sayfalar bu geliştirme desenlerini destekler. Daha fazla bilgi için bkz . Altyapı kalıcılık katmanını tasarlama ve ASP.NET Core'da test denetleyicisi mantığı (örnek depo düzenini uygular).

Uygulama kuruluşunu test et

Test uygulaması tests/RazorPagesTestSample.Tests klasörünün içindeki bir konsol uygulamasıdır.

Test uygulaması klasörü Tanım
UnitTests
  • DataAccessLayerTest.cs DAL için birim testlerini içerir.
  • IndexPageTests.cs Dizin sayfası modeli için birim testlerini içerir.
Yardımcı program Veritabanının TestDbContextOptions her test için temel koşuluna sıfırlanması için her DAL birim testi için yeni veritabanı bağlam seçenekleri oluşturmak için kullanılan yöntemi içerir.

Test çerçevesi xUnit'tir. Nesne sahte çerçevesi Moq'tır.

Veri erişim katmanının birim testleri (DAL)

İleti uygulaması, sınıfında (src/RazorPagesTestSample/Data/AppDbContext.cs) bulunan dört yöntem içeren bir DAL'a AppDbContext sahiptir. Her yöntemin test uygulamasında bir veya iki birim testi vardır.

DAL yöntemi Function
GetMessagesAsync özelliğine göre sıralanmış veritabanından Text bir List<Message> alır.
AddMessageAsync Veritabanına bir Message ekler.
DeleteAllMessagesAsync Veritabanındaki tüm Message girdileri siler.
DeleteMessageAsync veritabanından tek Message bir öğesini tarafından Idsiler.

DAL'nin birim testleri, her test için yeni AppDbContext bir oluşturma işlemini gerektirirDbContextOptions. Her test için oluşturma DbContextOptions yaklaşımlarından biri kullanmaktır DbContextOptionsBuilder:

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

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

Bu yaklaşımdaki sorun, her testin veritabanını önceki testte bıraktığı durumda almasıdır. Bu, birbiriyle karışmadan atomik birim testleri yazmaya çalışırken sorun olabilir. öğesini her test için yeni bir veritabanı bağlamı kullanmaya zorlamak AppDbContext için yeni bir hizmet sağlayıcısını temel alan bir DbContextOptions örnek sağlayın. Test uygulaması, bunun sınıf yöntemini TestDbContextOptions (tests/RazorPagesTestSample.Tests/Utilities/Utilities.cs) kullanarak Utilities nasıl yapılacağını gösterir:

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

DAL birim testlerinde komutunun DbContextOptions kullanılması, her testin yeni bir veritabanı örneğiyle atomik olarak çalışmasına olanak tanır:

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

(UnitTests/DataAccessLayerTest.cs) sınıfındaki DataAccessLayerTest her test yöntemi benzer bir Arrange-Act-Assert desenini izler:

  1. Düzenleme: Veritabanı test için yapılandırılır ve/veya beklenen sonuç tanımlanır.
  2. Eylem: Test yürütülür.
  3. Onay: Test sonucunun başarılı olup olmadığını belirlemek için onaylar yapılır.

Örneğin yöntemi, DeleteMessageAsync (src/RazorPagesTestSample/Data/AppDbContext.cs) tarafından Id tanımlanan tek bir iletiyi kaldırmakla sorumludur:

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

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

Bu yöntem için iki test vardır. Bir test, ileti veritabanında mevcut olduğunda yöntemin bir iletiyi silip silmediğini denetler. Diğer yöntem, silme iletisi Id yoksa veritabanının değişmediğini test eder. DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound yöntemi aşağıda gösterilmiştir:

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

İlk olarak, yöntem Düzenleme adımını gerçekleştirir ve burada Act adımı için hazırlık gerçekleştirilir. Tohumlama iletileri alınır ve içinde seedMessagestutulur. Tohumlama iletileri veritabanına kaydedilir. içeren ileti Id1 silinmek üzere ayarlanmıştır. DeleteMessageAsync yöntemi yürütülürken, beklenen iletilerde , ile olan dışında Id1tüm iletiler bulunmalıdır. değişkeni bu expectedMessages beklenen sonucu temsil eder.

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

yöntemi şu şekilde davranır: DeleteMessageAsync yöntemi, öğesini geçirerek recId1yürütülür:

// Act
await db.DeleteMessageAsync(recId);

Son olarak, yöntemi bağlamdan öğesini alır Messages ve ikisinin expectedMessages eşit olduğunu onaylama işlemiyle karşılaştırır:

// 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));

İkisinin aynı olduğunu List<Message> karşılaştırmak için:

  • İletiler tarafından Idsıralanır.
  • İleti çiftleri özelliğinde Text karşılaştırılır.

Benzer bir test yöntemi, DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound var olmayan bir iletiyi silme girişiminin sonucunu denetler. Bu durumda, yöntem yürütüldükten sonra veritabanında beklenen iletiler gerçek iletilere DeleteMessageAsync eşit olmalıdır. Veritabanının içeriğinde değişiklik yapılmamalıdır:

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

Sayfa modeli yöntemlerinin birim testleri

Başka bir birim testi kümesi de sayfa modeli yöntemlerinin testlerinden sorumludur. İleti uygulamasında, Dizin sayfası modelleri sınıfında IndexModelsrc/RazorPagesTestSample/Pages/Index.cshtml.csbulunur.

Sayfa modeli yöntemi Function
OnGetAsync yöntemini kullanarak GetMessagesAsync kullanıcı arabirimi için DAL'den iletileri alır.
OnPostAddMessageAsync ModelState geçerliyse, veritabanına ileti eklemek için çağrılarAddMessageAsync.
OnPostDeleteAllMessagesAsync Veritabanındaki tüm iletileri silmek için çağrılar DeleteAllMessagesAsync .
OnPostDeleteMessageAsync DeleteMessageAsync Belirtilen ile bir iletiyi silmek için yürütürId.
OnPostAnalyzeMessagesAsync Veritabanında bir veya daha fazla ileti varsa, ileti başına ortalama sözcük sayısını hesaplar.

Sayfa modeli yöntemleri(tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs sınıfında yedi test IndexPageTests kullanılarak test edilir. Testler tanıdık Düzenleme-Assert-Act desenini kullanır. Bu testler şunlara odaklanır:

  • ModelState geçersiz olduğunda yöntemlerin doğru davranışı izleyip izlemediğini belirleme.
  • Yöntemlerin doğru şekilde üretildiğini IActionResultonaylama.
  • Özellik değeri atamalarının doğru yapılarak denetleniyor.

Bu test grubu genellikle bir sayfa modeli yönteminin yürütüldüğü Act adımı için beklenen verileri üretmek üzere DAL yöntemleriyle dalga geçer. Örneğin, GetMessagesAsync yöntemi AppDbContext çıkış üretmek için sahtedir. Bir sayfa modeli yöntemi bu yöntemi yürüttüğünde, sahte sonuç döndürür. Veriler veritabanından gelmez. Bu, DAL'yi sayfa modeli testlerinde kullanmak için öngörülebilir, güvenilir test koşulları oluşturur.

Test, OnGetAsync_PopulatesThePageModel_WithAListOfMessages yöntemin GetMessagesAsync sayfa modeli için nasıl sahte olduğunu gösterir:

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 Yöntem Act adımında yürütülürken sayfa modelinin GetMessagesAsync yöntemini çağırır.

Birim testi Yasası adımı (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage sayfa modelinin OnGetAsync yöntemi (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

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

GetMessagesAsync DAL içindeki yöntemi, bu yöntem çağrısının sonucunu döndürmez. Yöntemin sahte sürümü sonucu döndürür.

Assert Adımda, gerçek iletiler (actualMessages) sayfa modelinin Messages özelliğinden atanır. İletiler atandığında bir tür denetimi de gerçekleştirilir. Beklenen ve gerçek iletiler özellikleriyle Text karşılaştırılır. Test, iki List<Message> örneğin aynı iletileri içerdiğini onaylar.

// 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));

Bu gruptaki diğer testler, , ActionContextModelStateDictionaryViewDataDictionarybir ve PageContextoluşturmak için PageContext, , içeren sayfa modeli nesneleri DefaultHttpContextoluşturur. Bunlar, testlerin yürütülmesinde yararlıdır. Örneğin, ileti uygulaması yürütülürken OnPostAddMessageAsync geçerli PageResult bir döndürüldüğünü denetlemek için ile AddModelError bir ModelState hata oluşturur:

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

Ek kaynaklar

ASP.NET Core, Pages uygulamalarının birim testlerini Razor destekler. Veri erişim katmanı (DAL) ve sayfa modellerinin testleri şunların sağlanmasına yardımcı olur:

  • Sayfalar uygulamasının Razor bölümleri, uygulama oluşturma sırasında bağımsız olarak ve bir birim olarak birlikte çalışır.
  • Sınıflar ve yöntemler sınırlı sorumluluk kapsamlarına sahiptir.
  • Uygulamanın nasıl davranması gerektiğine ilişkin ek belgeler vardır.
  • Kod güncelleştirmelerinin getirdiği hatalar olan regresyonlar, otomatik derleme ve dağıtım sırasında bulunur.

Bu konu başlığında Sayfalar uygulamaları ve birim testleri hakkında Razor temel bilgilere sahip olduğunuz varsayılır. Sayfalar uygulamaları veya test kavramları hakkında Razor bilginiz yoksa aşağıdaki konulara bakın:

Örnek kodu görüntüleme veya indirme (indirme)

Örnek proje iki uygulamadan oluşur:

App Proje klasörü Tanım
İleti uygulaması src/RazorPagesTestSample Kullanıcının ileti eklemesine, bir iletiyi silmesine, tüm iletileri silmesine ve iletileri analiz etmesine izin verir (ileti başına ortalama sözcük sayısını bulur).
Test uygulaması tests/RazorPagesTestSample.Tests İleti uygulamasının DAL ve Dizin sayfası modelini birim testi için kullanılır.

Testler, Visual Studio gibi bir IDE'nin yerleşik test özellikleri kullanılarak çalıştırılabilir. Visual Studio Code veya komut satırı kullanıyorsanız, tests/RazorPagesTestSample.Tests klasöründeki bir komut isteminde aşağıdaki komutu yürütebilirsiniz:

dotnet test

İleti uygulaması kuruluşu

İleti uygulaması, aşağıdaki özelliklere sahip bir Razor Sayfalar ileti sistemidir:

  • Uygulamanın (Pages/Index.cshtml ve Pages/Index.cshtml.cs) Dizin sayfası, iletilerin eklenmesini, silinmesini ve analizini denetlemek için bir kullanıcı arabirimi ve sayfa modeli yöntemleri sağlar (ileti başına ortalama sözcük sayısını bulur).
  • bir ileti sınıfıData/Message.cs () tarafından Message iki özelliğe sahip olarak açıklanır: Id (anahtar) ve Text (ileti). Text özelliği gereklidir ve 200 karakterle sınırlıdır.
  • İletiler Entity Framework'ün bellek içi veritabanı† kullanılarak depolanır.
  • Uygulama, veritabanı bağlam sınıfında AppDbContextData/AppDbContext.cs() bir DAL içerir. DAL yöntemleri, testlerde kullanılmak üzere yöntemlerin sahtesini oluşturmanızı sağlayan olarak işaretlenir virtual.
  • Uygulama başlangıcında veritabanı boşsa, ileti deposu üç iletiyle başlatılır. Bu dağıtılmış iletiler testlerde de kullanılır.

†InMemory ile Test et ef konusu, MSTest ile testler için bellek içi veritabanının nasıl kullanılacağını açıklar. Bu konu, xUnit test çerçevesini kullanır. Farklı test çerçevelerindeki test kavramları ve test uygulamaları benzerdir ancak aynı değildir.

Örnek uygulama depo desenini kullanmasa ve çalışma birimi (UoW) deseninin etkili bir örneği olmasa da, Razor Sayfalar bu geliştirme desenlerini destekler. Daha fazla bilgi için bkz . Altyapı kalıcılık katmanını tasarlama ve ASP.NET Core'da test denetleyicisi mantığı (örnek depo düzenini uygular).

Uygulama kuruluşunu test et

Test uygulaması tests/RazorPagesTestSample.Tests klasörünün içindeki bir konsol uygulamasıdır.

Test uygulaması klasörü Tanım
UnitTests
  • DataAccessLayerTest.cs DAL için birim testlerini içerir.
  • IndexPageTests.cs Dizin sayfası modeli için birim testlerini içerir.
Yardımcı program Veritabanının TestDbContextOptions her test için temel koşuluna sıfırlanması için her DAL birim testi için yeni veritabanı bağlam seçenekleri oluşturmak için kullanılan yöntemi içerir.

Test çerçevesi xUnit'tir. Nesne sahte çerçevesi Moq'tır.

Veri erişim katmanının birim testleri (DAL)

İleti uygulaması, sınıfında (src/RazorPagesTestSample/Data/AppDbContext.cs) bulunan dört yöntem içeren bir DAL'a AppDbContext sahiptir. Her yöntemin test uygulamasında bir veya iki birim testi vardır.

DAL yöntemi Function
GetMessagesAsync özelliğine göre sıralanmış veritabanından Text bir List<Message> alır.
AddMessageAsync Veritabanına bir Message ekler.
DeleteAllMessagesAsync Veritabanındaki tüm Message girdileri siler.
DeleteMessageAsync veritabanından tek Message bir öğesini tarafından Idsiler.

DAL'nin birim testleri, her test için yeni AppDbContext bir oluşturma işlemini gerektirirDbContextOptions. Her test için oluşturma DbContextOptions yaklaşımlarından biri kullanmaktır DbContextOptionsBuilder:

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

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

Bu yaklaşımdaki sorun, her testin veritabanını önceki testte bıraktığı durumda almasıdır. Bu, birbiriyle karışmadan atomik birim testleri yazmaya çalışırken sorun olabilir. öğesini her test için yeni bir veritabanı bağlamı kullanmaya zorlamak AppDbContext için yeni bir hizmet sağlayıcısını temel alan bir DbContextOptions örnek sağlayın. Test uygulaması, bunun sınıf yöntemini TestDbContextOptions (tests/RazorPagesTestSample.Tests/Utilities/Utilities.cs) kullanarak Utilities nasıl yapılacağını gösterir:

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

DAL birim testlerinde komutunun DbContextOptions kullanılması, her testin yeni bir veritabanı örneğiyle atomik olarak çalışmasına olanak tanır:

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

(UnitTests/DataAccessLayerTest.cs) sınıfındaki DataAccessLayerTest her test yöntemi benzer bir Arrange-Act-Assert desenini izler:

  1. Düzenleme: Veritabanı test için yapılandırılır ve/veya beklenen sonuç tanımlanır.
  2. Eylem: Test yürütülür.
  3. Onay: Test sonucunun başarılı olup olmadığını belirlemek için onaylar yapılır.

Örneğin yöntemi, DeleteMessageAsync (src/RazorPagesTestSample/Data/AppDbContext.cs) tarafından Id tanımlanan tek bir iletiyi kaldırmakla sorumludur:

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

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

Bu yöntem için iki test vardır. Bir test, ileti veritabanında mevcut olduğunda yöntemin bir iletiyi silip silmediğini denetler. Diğer yöntem, silme iletisi Id yoksa veritabanının değişmediğini test eder. DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound yöntemi aşağıda gösterilmiştir:

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

İlk olarak, yöntem Düzenleme adımını gerçekleştirir ve burada Act adımı için hazırlık gerçekleştirilir. Tohumlama iletileri alınır ve içinde seedMessagestutulur. Tohumlama iletileri veritabanına kaydedilir. içeren ileti Id1 silinmek üzere ayarlanmıştır. DeleteMessageAsync yöntemi yürütülürken, beklenen iletilerde , ile olan dışında Id1tüm iletiler bulunmalıdır. değişkeni bu expectedMessages beklenen sonucu temsil eder.

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

yöntemi şu şekilde davranır: DeleteMessageAsync yöntemi, öğesini geçirerek recId1yürütülür:

// Act
await db.DeleteMessageAsync(recId);

Son olarak, yöntemi bağlamdan öğesini alır Messages ve ikisinin expectedMessages eşit olduğunu onaylama işlemiyle karşılaştırır:

// 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));

İkisinin aynı olduğunu List<Message> karşılaştırmak için:

  • İletiler tarafından Idsıralanır.
  • İleti çiftleri özelliğinde Text karşılaştırılır.

Benzer bir test yöntemi, DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound var olmayan bir iletiyi silme girişiminin sonucunu denetler. Bu durumda, yöntem yürütüldükten sonra veritabanında beklenen iletiler gerçek iletilere DeleteMessageAsync eşit olmalıdır. Veritabanının içeriğinde değişiklik yapılmamalıdır:

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

Sayfa modeli yöntemlerinin birim testleri

Başka bir birim testi kümesi de sayfa modeli yöntemlerinin testlerinden sorumludur. İleti uygulamasında, Dizin sayfası modelleri sınıfında IndexModelsrc/RazorPagesTestSample/Pages/Index.cshtml.csbulunur.

Sayfa modeli yöntemi Function
OnGetAsync yöntemini kullanarak GetMessagesAsync kullanıcı arabirimi için DAL'den iletileri alır.
OnPostAddMessageAsync ModelState geçerliyse, veritabanına ileti eklemek için çağrılarAddMessageAsync.
OnPostDeleteAllMessagesAsync Veritabanındaki tüm iletileri silmek için çağrılar DeleteAllMessagesAsync .
OnPostDeleteMessageAsync DeleteMessageAsync Belirtilen ile bir iletiyi silmek için yürütürId.
OnPostAnalyzeMessagesAsync Veritabanında bir veya daha fazla ileti varsa, ileti başına ortalama sözcük sayısını hesaplar.

Sayfa modeli yöntemleri(tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs sınıfında yedi test IndexPageTests kullanılarak test edilir. Testler tanıdık Düzenleme-Assert-Act desenini kullanır. Bu testler şunlara odaklanır:

  • ModelState geçersiz olduğunda yöntemlerin doğru davranışı izleyip izlemediğini belirleme.
  • Yöntemlerin doğru şekilde üretildiğini IActionResultonaylama.
  • Özellik değeri atamalarının doğru yapılarak denetleniyor.

Bu test grubu genellikle bir sayfa modeli yönteminin yürütüldüğü Act adımı için beklenen verileri üretmek üzere DAL yöntemleriyle dalga geçer. Örneğin, GetMessagesAsync yöntemi AppDbContext çıkış üretmek için sahtedir. Bir sayfa modeli yöntemi bu yöntemi yürüttüğünde, sahte sonuç döndürür. Veriler veritabanından gelmez. Bu, DAL'yi sayfa modeli testlerinde kullanmak için öngörülebilir, güvenilir test koşulları oluşturur.

Test, OnGetAsync_PopulatesThePageModel_WithAListOfMessages yöntemin GetMessagesAsync sayfa modeli için nasıl sahte olduğunu gösterir:

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 Yöntem Act adımında yürütülürken sayfa modelinin GetMessagesAsync yöntemini çağırır.

Birim testi Yasası adımı (tests/RazorPagesTestSample.Tests/UnitTests/IndexPageTests.cs):

// Act
await pageModel.OnGetAsync();

IndexPage sayfa modelinin OnGetAsync yöntemi (src/RazorPagesTestSample/Pages/Index.cshtml.cs):

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

GetMessagesAsync DAL içindeki yöntemi, bu yöntem çağrısının sonucunu döndürmez. Yöntemin sahte sürümü sonucu döndürür.

Assert Adımda, gerçek iletiler (actualMessages) sayfa modelinin Messages özelliğinden atanır. İletiler atandığında bir tür denetimi de gerçekleştirilir. Beklenen ve gerçek iletiler özellikleriyle Text karşılaştırılır. Test, iki List<Message> örneğin aynı iletileri içerdiğini onaylar.

// 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));

Bu gruptaki diğer testler, , ActionContextModelStateDictionaryViewDataDictionarybir ve PageContextoluşturmak için PageContext, , içeren sayfa modeli nesneleri DefaultHttpContextoluşturur. Bunlar, testlerin yürütülmesinde yararlıdır. Örneğin, ileti uygulaması yürütülürken OnPostAddMessageAsync geçerli PageResult bir döndürüldüğünü denetlemek için ile AddModelError bir ModelState hata oluşturur:

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

Ek kaynaklar