Aracılığıyla paylaş


ASP.NET Core'da tümleştirme testleri

Jos van der Til, Martin Costello ve Javier Calvarro Nelson tarafından.

Tümleştirme testleri, bir uygulamanın bileşenlerinin veritabanı, dosya sistemi ve ağ gibi uygulamanın destekleyici altyapısını içeren bir düzeyde düzgün çalışmasını sağlar. ASP.NET Core, test web konağı ve bellek içi test sunucusu ile birim test çerçevesi kullanarak tümleştirme testlerini destekler.

Bu makalede, birim testleri hakkında temel bilgiler olduğu varsayılır. Test kavramlarını bilmiyorsanız .NET Core ve .NET Standard'da Birim Testi makalesine ve bağlantılı içeriğine bakın.

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

Örnek uygulama bir Razor Sayfalar uygulamasıdır ve Sayfalar hakkında temel bir anlayış olduğunu Razor varsayar. Pages'ı tanımıyorsanız Razor aşağıdaki makalelere bakın:

SPA'ları test etme amacıyla tarayıcıyı otomatikleştirebilen .NET için Playwright gibi bir araç öneririz.

Tümleştirme testlerine giriş

Tümleştirme testleri, bir uygulamanın bileşenlerini birim testlerinden daha geniş bir düzeyde değerlendirir. Birim testleri, tek tek sınıf yöntemleri gibi yalıtılmış yazılım bileşenlerini test etmek için kullanılır. Tümleştirme testleri, iki veya daha fazla uygulama bileşeninin birlikte çalışarak beklenen bir sonuç elde ettiğini ve büyük olasılıkla bir isteği tam olarak işlemek için gereken her bileşenin de dahil olduğunu onaylar.

Bu daha geniş testler, genellikle aşağıdaki bileşenler de dahil olmak üzere uygulamanın altyapısını ve tüm çerçevesini test etmek için kullanılır:

  • Veritabanı
  • Dosya sistemi
  • Ağ gereçleri
  • İstek-yanıt işlem hattı

Birim testleri, altyapı bileşenleri yerine sahte veya sahte nesneler olarak bilinen fabrikatlanmış bileşenleri kullanır.

Birim testlerinin aksine tümleştirme testleri:

  • Uygulamanın üretimde kullandığı gerçek bileşenleri kullanın.
  • Daha fazla kod ve veri işleme gerektirir.
  • Çalıştırılması daha uzun sürer.

Bu nedenle, tümleştirme testlerinin kullanımını en önemli altyapı senaryolarıyla sınırlayın. Bir davranış birim testi veya tümleştirme testi kullanılarak test edilebiliyorsa birim testini seçin.

Tümleştirme testlerinin tartışmalarında, test edilen projeye genellikle Test Altındaki Sistem veya kısaca "SUT" adı verilir. "SUT" bu makalede test edilen ASP.NET Core uygulamasına başvurmak için kullanılır.

Veritabanları ve dosya sistemleriyle verilerin ve dosya erişiminin her permütasyonu için tümleştirme testleri yazmayın. Bir uygulama genelinde veritabanları ve dosya sistemleriyle kaç yerde etkileşim kurduğundan bağımsız olarak, odaklanmış bir dizi okuma, yazma, güncelleştirme ve silme tümleştirme testi genellikle veritabanı ve dosya sistemi bileşenlerini yeterince test edebilmektedir. Bu bileşenlerle etkileşim kuran yöntem mantığının rutin testleri için birim testlerini kullanın. Birim testlerinde, altyapı sahteleri veya sahteleri kullanımı test yürütmenin daha hızlı gerçekleşmesine neden olur.

ASP.NET Core tümleştirme testleri

ASP.NET Core'daki tümleştirme testleri aşağıdakileri gerektirir:

  • Testleri içermek ve yürütmek için bir test projesi kullanılır. Test projesinin SUT başvurusu vardır.
  • Test projesi SUT için bir test web konağı oluşturur ve SUT ile istekleri ve yanıtları işlemek için bir test sunucusu istemcisi kullanır.
  • Testleri yürütmek ve test sonuçlarını raporlamak için bir test çalıştırıcısı kullanılır.

Tümleştirme testleri, her zamanki Düzenleme, Eylem ve Onay testi adımlarını içeren bir dizi olayı izler:

  1. SUT'nin web konağı yapılandırıldı.
  2. Uygulamaya istek göndermek için bir test sunucusu istemcisi oluşturulur.
  3. Test düzenleme adımı yürütülür: Test uygulaması bir istek hazırlar.
  4. Eylem test adımı yürütülür: İstemci isteği gönderir ve yanıtı alır.
  5. Assert test adımı yürütülür: Gerçek yanıt, beklenen yanıta göre geçiş veya başarısız olarak doğrulanır.
  6. Tüm testler yürütülene kadar işlem devam eder.
  7. Test sonuçları bildirilir.

Genellikle test web konağı, test çalıştırmaları için uygulamanın normal web ana bilgisayarından farklı yapılandırılır. Örneğin, testler için farklı bir veritabanı veya farklı uygulama ayarları kullanılabilir.

Test web konağı ve bellek içi test sunucusu ()TestServer gibi altyapı bileşenleri Microsoft.AspNetCore.Mvc.Testing paketi tarafından sağlanır veya yönetilir. Bu paketin kullanımı test oluşturma ve yürütmeyi kolaylaştırır.

Paket Microsoft.AspNetCore.Mvc.Testing aşağıdaki görevleri işler:

Birim testleri belgelerinde bir test projesi ve test çalıştırıcısının nasıl ayarlanacağı ve testleri çalıştırma hakkında ayrıntılı yönergelerin yanı sıra testleri ve test sınıflarını adlandırmaya yönelik öneriler açıklanmaktadır.

Birim testlerini tümleştirme testlerinden farklı projelere ayırın. Testleri ayırma:

  • Altyapı testi bileşenlerinin birim testlerine yanlışlıkla dahil edilmesini sağlamaya yardımcı olur.
  • Hangi test kümesinin çalıştırıldığı üzerinde denetime izin verir.

Sayfalar uygulamalarının ve MVC uygulamalarının testlerinin Razor yapılandırması arasında neredeyse hiçbir fark yoktur. Tek fark, testlerin nasıl adlandırıldığındandır. Razor Sayfalar uygulamasında, sayfa uç noktalarının testleri genellikle sayfa modeli sınıfından (örneğin, IndexPageTests Dizin sayfası için bileşen tümleştirmesini test etmek için) adlandırılır. MVC uygulamasında testler genellikle denetleyici sınıflarına göre düzenlenir ve test ettikleri denetleyicilerden (örneğin, HomeControllerTests denetleyici için bileşen tümleştirmesini Home test etmek için) adlandırılır.

Uygulama önkoşullarını test edin

Test projesinin aşağıdakileri yapması gerekir:

Bu önkoşullar örnek uygulamada görülebilir. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Dosyayı inceleyin. Örnek uygulama xUnit test çerçevesini ve AngleSharp ayrıştırıcı kitaplığını kullandığından örnek uygulama da aşağıdakilere başvurur:

Sürüm 2.4.2 veya üzerini kullanan xunit.runner.visualstudio uygulamalarda test projesinin pakete başvurması Microsoft.NET.Test.Sdk gerekir.

Entity Framework Core, testlerde de kullanılır. GitHub'daki proje dosyasına bakın.

SUT ortamı

SUT'nin ortamı ayarlı değilse, ortam varsayılan olarak Geliştirme olarak ayarlanır.

Varsayılan WebApplicationFactory ile temel testler

Aşağıdakilerden birini yaparak örtük olarak tanımlanmış Program sınıfı test projesinde kullanıma sunma:

  • Web uygulamasından test projesine iç türleri kullanıma sunma. Bu işlem SUT projesinin dosyasında (.csproj):

    <ItemGroup>
         <InternalsVisibleTo Include="MyTestProject" />
    </ItemGroup>
    
  • Program Kısmi bir sınıf bildirimi kullanarak sınıfını genel yapın:

    var builder = WebApplication.CreateBuilder(args);
    // ... Configure services, routes, etc.
    app.Run();
    + public partial class Program { }
    

    Örnek uygulama kısmi sınıf yaklaşımını Program kullanır.

WebApplicationFactory<TEntryPoint> tümleştirme testlerine yönelik bir TestServer oluşturmak için kullanılır. TEntryPoint genellikle Program.csSUT'nin giriş noktası sınıfıdır.

Test sınıfları, sınıfın testleri içerdiğini belirtmek ve sınıftaki testler arasında paylaşılan nesne örnekleri sağlamak için bir sınıf fikstür arabirimi (IClassFixture) uygular.

Aşağıdaki test sınıfı, BasicTestsSUT'yi önyüklemek ve bir test yöntemi Get_EndpointsReturnSuccessAndCorrectContentTypesağlamak HttpClient için öğesini kullanırWebApplicationFactory. yöntemi, yanıt durum kodunun başarılı olduğunu (200-299) ve Content-Type üst bilgi de birkaç uygulama sayfası için doğrular text/html; charset=utf-8 .

CreateClient() otomatik olarak yeniden yönlendirmeleri izleyen ve tanımlama bilgilerini işleyen bir örneği HttpClient oluşturur.

public class BasicTests 
    : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public BasicTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }

    [Theory]
    [InlineData("/")]
    [InlineData("/Index")]
    [InlineData("/About")]
    [InlineData("/Privacy")]
    [InlineData("/Contact")]
    public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
    {
        // Arrange
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync(url);

        // Assert
        response.EnsureSuccessStatusCode(); // Status Code 200-299
        Assert.Equal("text/html; charset=utf-8", 
            response.Content.Headers.ContentType.ToString());
    }
}

Varsayılan olarak, Genel Veri Koruma Yönetmeliği onay ilkesi etkinleştirildiğinde gerekli olmayan tanımlama bilgileri istekler arasında korunmaz. TempData sağlayıcısı tarafından kullanılanlar gibi temel olmayan tanımlama bilgilerini korumak için bunları testlerinizde temel olarak işaretleyin. Temel olarak işaretleme cookie yönergeleri için bkz . Temel tanımlama bilgileri.

AngleSharp ile Application Parts antiforgery denetimleri karşılaştırması

Bu makalede, sayfaları yükleyerek ve HTML ayrıştırarak antiforgery denetimlerini işlemek için AngleSharp ayrıştırıcısı kullanılır. Denetleyici ve Razor Sayfalar görünümlerinin uç noktalarını tarayıcıda nasıl işlediklerine önem vermeden daha düşük bir düzeyde test için kullanmayı Application Partsgöz önünde bulundurun. Uygulama Bölümleri yaklaşımı, uygulamaya gerekli değerleri almak için JSON isteklerinde bulunmak için kullanılabilecek bir denetleyici veya Razor Sayfa ekler. Daha fazla bilgi için Bkz. Tümleştirme Testi ASP.NET Uygulama Bölümlerini Kullanarak Sahtekarlık Önleme ile Korunan Temel Kaynaklar ve Martin Costello tarafından ilişkili GitHub deposu.

WebApplicationFactory'i özelleştirme

Web ana bilgisayar yapılandırması, bir veya daha fazla özel fabrika oluşturmak için öğesinden devralınarak test sınıflarından WebApplicationFactory<TEntryPoint> bağımsız olarak oluşturulabilir:

  1. öğesinden WebApplicationFactory devralın ve öğesini geçersiz kılın ConfigureWebHost. ile IWebHostBuilder hizmet koleksiyonunun yapılandırılmasına izin verir IWebHostBuilder.ConfigureServices

    public class CustomWebApplicationFactory<TProgram>
        : WebApplicationFactory<TProgram> where TProgram : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var dbContextDescriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbContextOptions<ApplicationDbContext>));
    
                services.Remove(dbContextDescriptor);
    
                var dbConnectionDescriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbConnection));
    
                services.Remove(dbConnectionDescriptor);
    
                // Create open SqliteConnection so EF won't automatically close it.
                services.AddSingleton<DbConnection>(container =>
                {
                    var connection = new SqliteConnection("DataSource=:memory:");
                    connection.Open();
    
                    return connection;
                });
    
                services.AddDbContext<ApplicationDbContext>((container, options) =>
                {
                    var connection = container.GetRequiredService<DbConnection>();
                    options.UseSqlite(connection);
                });
            });
    
            builder.UseEnvironment("Development");
        }
    }
    

    Örnek uygulamadaki veritabanı tohumlama yöntemi tarafından InitializeDbForTests gerçekleştirilir. Yöntemi, Tümleştirme testleri örneği: Test uygulaması kuruluşu bölümünde açıklanmıştır.

    SUT'nin veritabanı bağlamı içinde Program.cskaydedilir. Test uygulamasının builder.ConfigureServices geri çağırması, uygulamanın Program.cs kodu yürütüldükten sonra yürütülür. Testlerde uygulamanın veritabanından farklı bir veritabanı kullanmak için, uygulamanın veritabanı bağlamı içinde builder.ConfigureServicesdeğiştirilmelidir.

    Örnek uygulama, veritabanı bağlamı için hizmet tanımlayıcısını bulur ve hizmet kaydını kaldırmak için tanımlayıcıyı kullanır. Ardından fabrika, testler için bellek içi veritabanı kullanan yeni ApplicationDbContext bir ekliyor..

    Farklı bir veritabanına bağlanmak için öğesini DbConnectiondeğiştirin. SQL Server test veritabanını kullanmak için:

  1. Test sınıflarında özel CustomWebApplicationFactory öğesini kullanın. Aşağıdaki örnekte sınıfındaki IndexPageTests fabrika kullanılır:

    public class IndexPageTests :
        IClassFixture<CustomWebApplicationFactory<Program>>
    {
        private readonly HttpClient _client;
        private readonly CustomWebApplicationFactory<Program>
            _factory;
    
        public IndexPageTests(
            CustomWebApplicationFactory<Program> factory)
        {
            _factory = factory;
            _client = factory.CreateClient(new WebApplicationFactoryClientOptions
            {
                AllowAutoRedirect = false
            });
        }
    

    Örnek uygulamanın istemcisi, aşağıdaki yeniden yönlendirmelerin HttpClient engellenmesi için yapılandırılır. Sahte kimlik doğrulaması bölümünde daha sonra açıklandığı gibi bu, testlerin uygulamanın ilk yanıtının sonucunu denetlemesine izin verir. İlk yanıt, bu testlerin çoğunda üst bilgi içeren bir Location yeniden yönlendirmedir.

  2. Tipik bir test, isteği ve yanıtı işlemek için ve yardımcı yöntemlerini kullanır HttpClient :

    [Fact]
    public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
    {
        // Arrange
        var defaultPage = await _client.GetAsync("/");
        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
    
        //Act
        var response = await _client.SendAsync(
            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
            (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']"));
    
        // Assert
        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
        Assert.Equal("/", response.Headers.Location.OriginalString);
    }
    

SUT'ye yapılan herhangi bir POST isteği, uygulamanın veri koruma önleme sistemi tarafından otomatik olarak yapılan kötü amaçlı yazılımdan koruma denetimini karşılamalıdır. Testin POST isteğini düzenlemek için test uygulamasının şunları yapması gerekir:

  1. Sayfa için bir istekte bulun.
  2. Antiforgery'yi cookie ayrıştırın ve yanıttan doğrulama belirteci isteyin.
  3. POST isteğini sahteciliğe cookie karşı koruma ve istek doğrulama belirteci ile gerçekleştirin.

Örnek SendAsync uygulamadaki GetDocumentAsync yardımcı uzantı yöntemleri (Helpers/HttpClientExtensions.cs) ve yardımcı yöntemi (Helpers/HtmlHelpers.cs), aşağıdaki yöntemlerle antiforgery denetimini işlemek için AngleSharp ayrıştırıcısını kullanır:

  • GetDocumentAsync: alır HttpResponseMessage ve döndürür IHtmlDocument. GetDocumentAsync, özgün HttpResponseMessageöğesini temel alan bir sanal yanıt hazırlayan bir fabrika kullanır. Daha fazla bilgi için AngleSharp belgelerine bakın.
  • SendAsyncoluşturma HttpRequestMessage ve SUT'ye istek gönderme çağrısı SendAsync(HttpRequestMessage) için uzantı yöntemleriHttpClient. HTML formunu (IHtmlFormElement) ve aşağıdakileri kabul etmek için SendAsync aşırı yüklemeler:
    • Formun Gönder düğmesi (IHtmlElement)
    • Form değerleri koleksiyonu (IEnumerable<KeyValuePair<string, string>>)
    • Gönder düğmesi (IHtmlElement) ve form değerleri (IEnumerable<KeyValuePair<string, string>>)

AngleSharp, bu makalede ve örnek uygulamada gösterim amacıyla kullanılan bir üçüncü taraf ayrıştırma kitaplığıdır. AngleSharp, ASP.NET Core uygulamalarının tümleştirme testi için desteklenmez veya gerekli değildir. Html Çeviklik Paketi (HAP) gibi diğer ayrıştırıcılar kullanılabilir. Bir diğer yaklaşım da, sahteciliğe karşı koruma sisteminin istek doğrulama belirtecini ve kötü amaçlı yazılımdan korumayı doğrudan işlemek için kod yazmaktır cookie . Daha fazla bilgi için bu makaledeki AngleSharp vs Application Parts antiforgery denetimleri konusuna bakın.

EF-Core bellek içi veritabanı sağlayıcısı sınırlı ve temel test için kullanılabilir, ancak bellek içi test için önerilen seçenek SQLite sağlayıcısıdır.

Bkz . Başlangıç'ı başlangıç filtreleri ile genişletme. Bu, bir test özel hizmet veya ara yazılım gerektirdiğinde yararlı olan kullanarak IStartupFilterara yazılımı yapılandırmayı gösterir.

WithWebHostBuilder ile istemciyi özelleştirme

Bir test yönteminde ek yapılandırma gerektiğinde, WithWebHostBuilder yapılandırma tarafından daha da özelleştirilmiş bir ile yeni WebApplicationFactory bir IWebHostBuilder oluşturur.

Örnek kod, yapılandırılmış hizmetleri test saptamalarıyla değiştirmek için çağırırWithWebHostBuilder. Daha fazla bilgi ve örnek kullanım için bu makaledeki Sahte hizmetler ekleme bölümüne bakın.

Post_DeleteMessageHandler_ReturnsRedirectToRoot Örnek uygulamanın test yöntemi, kullanımını WithWebHostBuildergösterir. Bu test, SUT'da form gönderimini tetikleyerek veritabanında bir kayıt silme işlemi gerçekleştirir.

sınıfındaki IndexPageTests başka bir test veritabanındaki tüm kayıtları silen ve yönteminden önce Post_DeleteMessageHandler_ReturnsRedirectToRoot çalıştırabilen bir işlem gerçekleştirdiğinden, SUT'nin silmesi için bir kaydın mevcut olduğundan emin olmak için bu test yönteminde veritabanı yeniden görüntülenir. SUT'daki formun ilk sil düğmesinin messages seçilmesi, SUT isteğinde benzetim yapılır:

[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
    // Arrange
    using (var scope = _factory.Services.CreateScope())
    {
        var scopedServices = scope.ServiceProvider;
        var db = scopedServices.GetRequiredService<ApplicationDbContext>();

        Utilities.ReinitializeDbForTests(db);
    }

    var defaultPage = await _client.GetAsync("/");
    var content = await HtmlHelpers.GetDocumentAsync(defaultPage);

    //Act
    var response = await _client.SendAsync(
        (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
        (IHtmlButtonElement)content.QuerySelector("form[id='messages']")
            .QuerySelector("div[class='panel-body']")
            .QuerySelector("button"));

    // Assert
    Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
    Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
    Assert.Equal("/", response.Headers.Location.OriginalString);
}

İstemci seçenekleri

WebApplicationFactoryClientOptions Örnek oluştururken HttpClient varsayılanlar ve kullanılabilir seçenekler için sayfaya bakın.

sınıfını WebApplicationFactoryClientOptions oluşturun ve yöntemine CreateClient() geçirin:

public class IndexPageTests :
    IClassFixture<CustomWebApplicationFactory<Program>>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory<Program>
        _factory;

    public IndexPageTests(
        CustomWebApplicationFactory<Program> factory)
    {
        _factory = factory;
        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
    }

NOT: HTTPS Yeniden Yönlendirme Ara Yazılımı kullanılırken günlüklerde HTTPS yeniden yönlendirme uyarılarından kaçınmak için BaseAddress = new Uri("https://localhost")

Sahte hizmetler ekleme

Hizmetler, konak oluşturucusunda çağrısı ConfigureTestServices yapılan bir testte geçersiz kılınabilir. Geçersiz kılınan hizmetlerin kapsamını testin kendisi olarak tanımlamak için yöntemi bir WithWebHostBuilder konak oluşturucu almak için kullanılır. Bu durum aşağıdaki testlerde görülebilir:

Örnek SUT, teklif döndüren kapsamlı bir hizmet içerir. Teklif, Dizin sayfası istendiğinde Dizin sayfasındaki gizli bir alana eklenir.

Services/IQuoteService.cs:

public interface IQuoteService
{
    Task<string> GenerateQuote();
}

Services/QuoteService.cs:

// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
    public Task<string> GenerateQuote()
    {
        return Task.FromResult<string>(
            "Come on, Sarah. We've an appointment in London, " +
            "and we're already 30,000 years late.");
    }
}

Program.cs:

services.AddScoped<IQuoteService, QuoteService>();

Pages/Index.cshtml.cs:

public class IndexModel : PageModel
{
    private readonly ApplicationDbContext _db;
    private readonly IQuoteService _quoteService;

    public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
    {
        _db = db;
        _quoteService = quoteService;
    }

    [BindProperty]
    public Message Message { get; set; }

    public IList<Message> Messages { get; private set; }

    [TempData]
    public string MessageAnalysisResult { get; set; }

    public string Quote { get; private set; }

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

        Quote = await _quoteService.GenerateQuote();
    }

Pages/Index.cs:

<input id="quote" type="hidden" value="@Model.Quote">

SUT uygulaması çalıştırıldığında aşağıdaki işaretleme oluşturulur:

<input id="quote" type="hidden" value="Come on, Sarah. We&#x27;ve an appointment in 
    London, and we&#x27;re already 30,000 years late.">

Tümleştirme testinde hizmeti ve teklif eklemeyi test etmek için test tarafından SUT'ye sahte hizmet eklenir. Sahte hizmet, uygulamanın yerine test uygulaması QuoteService tarafından sağlanan ve adlı TestQuoteServicebir hizmettir:

IntegrationTests.IndexPageTests.cs:

// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
    public Task<string> GenerateQuote()
    {
        return Task.FromResult(
            "Something's interfering with time, Mr. Scarman, " +
            "and time is my business.");
    }
}

ConfigureTestServices çağrılır ve kapsamı belirlenmiş hizmet kaydedilir:

[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddScoped<IQuoteService, TestQuoteService>();
            });
        })
        .CreateClient();

    //Act
    var defaultPage = await client.GetAsync("/");
    var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
    var quoteElement = content.QuerySelector("#quote");

    // Assert
    Assert.Equal("Something's interfering with time, Mr. Scarman, " +
        "and time is my business.", quoteElement.Attributes["value"].Value);
}

Testin yürütülmesi sırasında oluşturulan işaretleme, tarafından TestQuoteServicesağlanan tırnak metnini yansıtır, böylece onay geçer:

<input id="quote" type="hidden" value="Something&#x27;s interfering with time, 
    Mr. Scarman, and time is my business.">

Sahte kimlik doğrulaması

Sınıfındaki AuthTests testler güvenli bir uç nokta olup olmadığını denetler:

  • Kimliği doğrulanmamış bir kullanıcıyı uygulamanın oturum açma sayfasına yönlendirir.
  • Kimliği doğrulanmış bir kullanıcının içeriğini döndürür.

SUT'de sayfa, /SecurePage sayfaya uygulamak için bir AuthorizePage AuthorizeFilter kural kullanır. Daha fazla bilgi için bkz Razor . Sayfa yetkilendirme kuralları.

services.AddRazorPages(options =>
{
    options.Conventions.AuthorizePage("/SecurePage");
});

Testte Get_SecurePageRedirectsAnUnauthenticatedUser , ayarı WebApplicationFactoryClientOptions olarak ayarlanarak AllowAutoRedirect yeniden yönlendirmelere izin vermemeye falseayarlanmıştır:

[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
    // Arrange
    var client = _factory.CreateClient(
        new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });

    // Act
    var response = await client.GetAsync("/SecurePage");

    // Assert
    Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
    Assert.StartsWith("http://localhost/Identity/Account/Login",
        response.Headers.Location.OriginalString);
}

İstemcinin yeniden yönlendirmeyi izlemesine izin vermeyerek aşağıdaki denetimler yapılabilir:

  • SUT tarafından döndürülen durum kodu, oturum açma sayfasına yeniden yönlendirmeden sonraki son durum koduna değil beklenen HttpStatusCode.Redirect sonucta göre denetlenebilir. Bu, olacaktır HttpStatusCode.OK.
  • Yanıt Location üst bilgilerindeki üst bilgi değeri, üst bilginin bulunmayacağı son oturum açma sayfası yanıtıyla değil ile Location başladığını http://localhost/Identity/Account/Loginonaylamak için denetlendi.

Test uygulaması, kimlik doğrulaması ve yetkilendirme özelliklerini test etmek için bir AuthenticationHandler<TOptions> ConfigureTestServices ile dalga geçebilir. En düşük senaryo bir AuthenticateResult.Successdöndürür:

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger, UrlEncoder encoder)
        : base(options, logger, encoder)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
        var identity = new ClaimsIdentity(claims, "Test");
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, "TestScheme");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

TestAuthHandler, kimlik doğrulama düzeni için kaydedildiği yere AddAuthentication ayarlandığında TestScheme kullanıcının kimliğini doğrulamak için ConfigureTestServicesçağrılır. Düzenin uygulamanızın TestScheme beklediği düzenle eşleşmesi önemlidir. Aksi takdirde kimlik doğrulaması çalışmaz.

[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddAuthentication(defaultScheme: "TestScheme")
                    .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
                        "TestScheme", options => { });
            });
        })
        .CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false,
        });

    client.DefaultRequestHeaders.Authorization =
        new AuthenticationHeaderValue(scheme: "TestScheme");

    //Act
    var response = await client.GetAsync("/SecurePage");

    // Assert
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

hakkında WebApplicationFactoryClientOptionsdaha fazla bilgi için İstemci seçenekleri bölümüne bakın.

Kimlik doğrulaması ara yazılımı için temel testler

Kimlik doğrulama ara yazılımının temel testleri için bu GitHub deposuna bakın. Test senaryosuna özgü bir test sunucusu içerir.

Ortamı ayarlama

Ortamı özel uygulama fabrikasında ayarlayın:

public class CustomWebApplicationFactory<TProgram>
    : WebApplicationFactory<TProgram> where TProgram : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var dbContextDescriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbContextOptions<ApplicationDbContext>));

            services.Remove(dbContextDescriptor);

            var dbConnectionDescriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbConnection));

            services.Remove(dbConnectionDescriptor);

            // Create open SqliteConnection so EF won't automatically close it.
            services.AddSingleton<DbConnection>(container =>
            {
                var connection = new SqliteConnection("DataSource=:memory:");
                connection.Open();

                return connection;
            });

            services.AddDbContext<ApplicationDbContext>((container, options) =>
            {
                var connection = container.GetRequiredService<DbConnection>();
                options.UseSqlite(connection);
            });
        });

        builder.UseEnvironment("Development");
    }
}

Test altyapısı uygulama içeriği kök yolunu nasıl çıkarsar?

Oluşturucu, WebApplicationFactory derlemeye eşit TEntryPoint bir WebApplicationFactoryContentRootAttribute anahtara sahip tümleştirme testlerini içeren derlemede öğesini arayarak uygulama içeriği kök yolunu çıkarsarSystem.Reflection.Assembly.FullName. Doğru anahtara sahip bir özniteliğin bulunamazsa, WebApplicationFactory çözüm dosyasını aramaya (.sln) geri döner ve derleme adını çözüm dizinine ekler TEntryPoint . Uygulama kök dizini (içerik kök yolu), görünümleri ve içerik dosyalarını bulmak için kullanılır.

Gölge kopyalamayı devre dışı bırakma

Gölge kopyalama, testlerin çıkış dizininden farklı bir dizinde yürütülmesine neden olur. Testleriniz ile ilgili Assembly.Location dosyaları yüklemeye bağlıysa ve sorunlarla karşılaşıyorsanız, gölge kopyalamayı devre dışı bırakmanız gerekebilir.

xUnit kullanırken gölge kopyalamayı devre dışı bırakmak için test projesi dizininizde doğru yapılandırma ayarıyla bir xunit.runner.json dosya oluşturun:

{
  "shadowCopy": false
}

Nesnelerin atılması

Uygulamanın testleri IClassFixture yürütüldükten TestServer ve HttpClient xUnit WebApplicationFactorytarafından atıldığında atılır. Geliştirici tarafından örneği oluşturulmuş nesneler elden çıkarılma gerektiriyorsa, bunları IClassFixture uygulamada atın. Daha fazla bilgi için bkz . Dispose yöntemi uygulama.

Tümleştirme testleri örneği

Örnek uygulama iki uygulamadan oluşur:

Uygulama Proje dizini Açıklama
İleti uygulaması (SUT) src/RazorPagesProject Kullanıcının bir ileti eklemesine, silmesine, tümünü silmesine ve iletileri analiz etmesine izin verir.
Test uygulaması tests/RazorPagesProject.Tests SUT'yi tümleştirme 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, dizindeki bir komut isteminde aşağıdaki komutu yürütürtests/RazorPagesProject.Tests:

dotnet test

İleti uygulaması (SUT) kuruluşu

SUT, 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 (ileti başına ortalama sözcükler) denetlemek için bir kullanıcı arabirimi ve sayfa modeli yöntemleri sağlar.
  • 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 AppDbContext () bir veri erişim katmanı (Data/AppDbContext.csDAL) içerir.
  • Uygulama başlangıcında veritabanı boşsa, ileti deposu üç iletiyle başlatılır.
  • Uygulama yalnızca kimliği doğrulanmış bir /SecurePage kullanıcı tarafından erişilebilen bir içerir.

† InMemory ile test etme ef makalesi, 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.

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 Test denetleyicisi mantığı (örnek, depo düzenini uygular).

Uygulama kuruluşunu test et

Test uygulaması, dizinin içindeki tests/RazorPagesProject.Tests bir konsol uygulamasıdır.

Uygulama dizinini test et Açıklama
AuthTests Aşağıdakiler için test yöntemlerini içerir:
  • Kimliği doğrulanmamış bir kullanıcı tarafından güvenli bir sayfaya erişme.
  • Kimliği doğrulanmış bir kullanıcı tarafından sahtesiyle AuthenticationHandler<TOptions>güvenli bir sayfaya erişme.
  • GitHub kullanıcı profili alma ve profilin kullanıcı oturum açma bilgilerini denetleme.
BasicTests Yönlendirme ve içerik türü için bir test yöntemi içerir.
IntegrationTests Özel WebApplicationFactory sınıf kullanarak Dizin sayfasının tümleştirme testlerini içerir.
Helpers/Utilities
  • Utilities.csInitializeDbForTests test verileriyle veritabanının tohumunu oluşturmak için kullanılan yöntemi içerir.
  • HtmlHelpers.cs test yöntemleri tarafından kullanılmak üzere AngleSharp IHtmlDocument döndürmek için bir yöntem sağlar.
  • HttpClientExtensions.cs SUT'ye istek göndermek için için SendAsync aşırı yüklemeler sağlar.

Test çerçevesi xUnit'tir. Tümleştirme testleri, öğesini içeren TestServerkullanılarak Microsoft.AspNetCore.TestHostyapılır. Microsoft.AspNetCore.Mvc.Testing Paket test ana bilgisayarını ve test sunucusunu yapılandırmak için kullanıldığından TestHost ve TestServer paketleri, test uygulamasının proje dosyasında veya test uygulamasındaki geliştirici yapılandırmasında doğrudan paket başvuruları gerektirmez.

Tümleştirme testleri genellikle test yürütmeden önce veritabanında küçük bir veri kümesi gerektirir. Örneğin, silme testi veritabanı kaydı silme işlemini çağırır, bu nedenle silme isteğinin başarılı olması için veritabanında en az bir kayıt olmalıdır.

Örnek uygulama, testleri yürütürken kullanabileceği üç iletiyle Utilities.cs veritabanının çekirdeğini oluşturur:

public static void InitializeDbForTests(ApplicationDbContext db)
{
    db.Messages.AddRange(GetSeedingMessages());
    db.SaveChanges();
}

public static void ReinitializeDbForTests(ApplicationDbContext db)
{
    db.Messages.RemoveRange(db.Messages);
    InitializeDbForTests(db);
}

public static List<Message> GetSeedingMessages()
{
    return new List<Message>()
    {
        new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
        new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
        new Message(){ Text = "TEST RECORD: To the rational mind, " +
            "nothing is inexplicable; only unexplained." }
    };
}

SUT'nin veritabanı bağlamı içinde Program.cskaydedilir. Test uygulamasının builder.ConfigureServices geri çağırması, uygulamanın Program.cs kodu yürütüldükten sonra yürütülür. Testlerde farklı bir veritabanı kullanmak için uygulamanın veritabanı bağlamı içinde builder.ConfigureServicesdeğiştirilmelidir. Daha fazla bilgi için WebApplicationFactory'yi Özelleştirme bölümüne bakın.

Ek kaynaklar

Bu konu, birim testlerinin temel bir anlayışını varsayar. Test kavramlarını bilmiyorsanız .NET Core ve .NET Standard'da Birim Testi konusuna ve bağlantılı içeriğine bakın.

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

Örnek uygulama bir Razor Sayfalar uygulamasıdır ve Sayfalar hakkında temel bir anlayış olduğunu Razor varsayar. Sayfalar'ı bilmiyorsanız Razor aşağıdaki konulara bakın:

Not

SPA'ları test etme amacıyla tarayıcıyı otomatikleştirebilen .NET için Playwright gibi bir araç öneririz.

Tümleştirme testlerine giriş

Tümleştirme testleri, bir uygulamanın bileşenlerini birim testlerinden daha geniş bir düzeyde değerlendirir. Birim testleri, tek tek sınıf yöntemleri gibi yalıtılmış yazılım bileşenlerini test etmek için kullanılır. Tümleştirme testleri, iki veya daha fazla uygulama bileşeninin birlikte çalışarak beklenen bir sonuç elde ettiğini ve büyük olasılıkla bir isteği tam olarak işlemek için gereken her bileşenin de dahil olduğunu onaylar.

Bu daha geniş testler, genellikle aşağıdaki bileşenler de dahil olmak üzere uygulamanın altyapısını ve tüm çerçevesini test etmek için kullanılır:

  • Veritabanı
  • Dosya sistemi
  • Ağ gereçleri
  • İstek-yanıt işlem hattı

Birim testleri, altyapı bileşenleri yerine sahte veya sahte nesneler olarak bilinen fabrikatlanmış bileşenleri kullanır.

Birim testlerinin aksine tümleştirme testleri:

  • Uygulamanın üretimde kullandığı gerçek bileşenleri kullanın.
  • Daha fazla kod ve veri işleme gerektirir.
  • Çalıştırılması daha uzun sürer.

Bu nedenle, tümleştirme testlerinin kullanımını en önemli altyapı senaryolarıyla sınırlayın. Bir davranış birim testi veya tümleştirme testi kullanılarak test edilebiliyorsa birim testini seçin.

Tümleştirme testlerinin tartışmalarında, test edilen projeye genellikle Test Altındaki Sistem veya kısaca "SUT" adı verilir. "SUT" bu makalede test edilen ASP.NET Core uygulamasına başvurmak için kullanılır.

Veritabanları ve dosya sistemleriyle verilerin ve dosya erişiminin her permütasyonu için tümleştirme testleri yazmayın. Bir uygulama genelinde veritabanları ve dosya sistemleriyle kaç yerde etkileşim kurduğundan bağımsız olarak, odaklanmış bir dizi okuma, yazma, güncelleştirme ve silme tümleştirme testi genellikle veritabanı ve dosya sistemi bileşenlerini yeterince test edebilmektedir. Bu bileşenlerle etkileşim kuran yöntem mantığının rutin testleri için birim testlerini kullanın. Birim testlerinde, altyapı sahteleri veya sahteleri kullanımı test yürütmenin daha hızlı gerçekleşmesine neden olur.

ASP.NET Core tümleştirme testleri

ASP.NET Core'daki tümleştirme testleri aşağıdakileri gerektirir:

  • Testleri içermek ve yürütmek için bir test projesi kullanılır. Test projesinin SUT başvurusu vardır.
  • Test projesi SUT için bir test web konağı oluşturur ve SUT ile istekleri ve yanıtları işlemek için bir test sunucusu istemcisi kullanır.
  • Testleri yürütmek ve test sonuçlarını raporlamak için bir test çalıştırıcısı kullanılır.

Tümleştirme testleri, her zamanki Düzenleme, Eylem ve Onay testi adımlarını içeren bir dizi olayı izler:

  1. SUT'nin web konağı yapılandırıldı.
  2. Uygulamaya istek göndermek için bir test sunucusu istemcisi oluşturulur.
  3. Test düzenleme adımı yürütülür: Test uygulaması bir istek hazırlar.
  4. Eylem test adımı yürütülür: İstemci isteği gönderir ve yanıtı alır.
  5. Assert test adımı yürütülür: Gerçek yanıt, beklenen yanıta göre geçiş veya başarısız olarak doğrulanır.
  6. Tüm testler yürütülene kadar işlem devam eder.
  7. Test sonuçları bildirilir.

Genellikle test web konağı, test çalıştırmaları için uygulamanın normal web ana bilgisayarından farklı yapılandırılır. Örneğin, testler için farklı bir veritabanı veya farklı uygulama ayarları kullanılabilir.

Test web konağı ve bellek içi test sunucusu ()TestServer gibi altyapı bileşenleri Microsoft.AspNetCore.Mvc.Testing paketi tarafından sağlanır veya yönetilir. Bu paketin kullanımı test oluşturma ve yürütmeyi kolaylaştırır.

Paket Microsoft.AspNetCore.Mvc.Testing aşağıdaki görevleri işler:

Birim testleri belgelerinde bir test projesi ve test çalıştırıcısının nasıl ayarlanacağı ve testleri çalıştırma hakkında ayrıntılı yönergelerin yanı sıra testleri ve test sınıflarını adlandırmaya yönelik öneriler açıklanmaktadır.

Birim testlerini tümleştirme testlerinden farklı projelere ayırın. Testleri ayırma:

  • Altyapı testi bileşenlerinin birim testlerine yanlışlıkla dahil edilmesini sağlamaya yardımcı olur.
  • Hangi test kümesinin çalıştırıldığı üzerinde denetime izin verir.

Sayfalar uygulamalarının ve MVC uygulamalarının testlerinin Razor yapılandırması arasında neredeyse hiçbir fark yoktur. Tek fark, testlerin nasıl adlandırıldığındandır. Razor Sayfalar uygulamasında, sayfa uç noktalarının testleri genellikle sayfa modeli sınıfından (örneğin, IndexPageTests Dizin sayfası için bileşen tümleştirmesini test etmek için) adlandırılır. MVC uygulamasında testler genellikle denetleyici sınıflarına göre düzenlenir ve test ettikleri denetleyicilerden (örneğin, HomeControllerTests denetleyici için bileşen tümleştirmesini Home test etmek için) adlandırılır.

Uygulama önkoşullarını test edin

Test projesinin aşağıdakileri yapması gerekir:

Bu önkoşullar örnek uygulamada görülebilir. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Dosyayı inceleyin. Örnek uygulama xUnit test çerçevesini ve AngleSharp ayrıştırıcı kitaplığını kullandığından örnek uygulama da aşağıdakilere başvurur:

Sürüm 2.4.2 veya üzerini kullanan xunit.runner.visualstudio uygulamalarda test projesinin pakete başvurması Microsoft.NET.Test.Sdk gerekir.

Entity Framework Core, testlerde de kullanılır. Uygulama şu başvurulara başvurur:

SUT ortamı

SUT'nin ortamı ayarlı değilse, ortam varsayılan olarak Geliştirme olarak ayarlanır.

Varsayılan WebApplicationFactory ile temel testler

WebApplicationFactory<TEntryPoint> tümleştirme testlerine yönelik bir TestServer oluşturmak için kullanılır. TEntryPoint genellikle sınıfı olan SUT'nin giriş noktası sınıfıdır Startup .

Test sınıfları, sınıfın testleri içerdiğini belirtmek ve sınıftaki testler arasında paylaşılan nesne örnekleri sağlamak için bir sınıf fikstür arabirimi (IClassFixture) uygular.

Aşağıdaki test sınıfı, BasicTestsSUT'yi önyüklemek ve bir test yöntemi Get_EndpointsReturnSuccessAndCorrectContentTypesağlamak HttpClient için öğesini kullanırWebApplicationFactory. yöntemi, yanıt durum kodunun başarılı olup olmadığını (200-299 aralığındaki durum kodları) ve Content-Type üst bilginin birkaç uygulama sayfası için olup olmadığını text/html; charset=utf-8 denetler.

CreateClient() otomatik olarak yeniden yönlendirmeleri izleyen ve tanımlama bilgilerini işleyen bir örneği HttpClient oluşturur.

public class BasicTests 
    : IClassFixture<WebApplicationFactory<RazorPagesProject.Startup>>
{
    private readonly WebApplicationFactory<RazorPagesProject.Startup> _factory;

    public BasicTests(WebApplicationFactory<RazorPagesProject.Startup> factory)
    {
        _factory = factory;
    }

    [Theory]
    [InlineData("/")]
    [InlineData("/Index")]
    [InlineData("/About")]
    [InlineData("/Privacy")]
    [InlineData("/Contact")]
    public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
    {
        // Arrange
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync(url);

        // Assert
        response.EnsureSuccessStatusCode(); // Status Code 200-299
        Assert.Equal("text/html; charset=utf-8", 
            response.Content.Headers.ContentType.ToString());
    }
}

Varsayılan olarak, GDPR onay ilkesi etkinleştirildiğinde gerekli olmayan tanımlama bilgileri istekler arasında korunmaz. TempData sağlayıcısı tarafından kullanılanlar gibi temel olmayan tanımlama bilgilerini korumak için bunları testlerinizde temel olarak işaretleyin. Temel olarak işaretleme cookie yönergeleri için bkz . Temel tanımlama bilgileri.

WebApplicationFactory'i özelleştirme

Web ana bilgisayar yapılandırması, bir veya daha fazla özel fabrika oluşturmak için öğesinden devralınarak test sınıflarından WebApplicationFactory bağımsız olarak oluşturulabilir:

  1. öğesinden WebApplicationFactory devralın ve öğesini geçersiz kılın ConfigureWebHost. ile IWebHostBuilder hizmet koleksiyonunun ConfigureServicesyapılandırılmasına izin verir:

    public class CustomWebApplicationFactory<TStartup>
        : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var descriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbContextOptions<ApplicationDbContext>));
    
                services.Remove(descriptor);
    
                services.AddDbContext<ApplicationDbContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                });
    
                var sp = services.BuildServiceProvider();
    
                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<ApplicationDbContext>();
                    var logger = scopedServices
                        .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
    
                    db.Database.EnsureCreated();
    
                    try
                    {
                        Utilities.InitializeDbForTests(db);
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, "An error occurred seeding the " +
                            "database with test messages. Error: {Message}", ex.Message);
                    }
                }
            });
        }
    }
    

    Örnek uygulamadaki veritabanı tohumlama yöntemi tarafından InitializeDbForTests gerçekleştirilir. Yöntemi, Tümleştirme testleri örneği: Test uygulaması kuruluşu bölümünde açıklanmıştır.

    SUT'nin veritabanı bağlamı yöntemine Startup.ConfigureServices kaydedilir. Test uygulamasının builder.ConfigureServices geri çağırması, uygulamanın Startup.ConfigureServices kodu yürütüldükten sonra yürütülür. Yürütme sırası, ASP.NET Core 3.0 sürümüyle Genel Ana Bilgisayar için hataya neden olan bir değişikliktir. Testlerde uygulamanın veritabanından farklı bir veritabanı kullanmak için, uygulamanın veritabanı bağlamı içinde builder.ConfigureServicesdeğiştirilmelidir.

    Web Konağını kullanmaya devam eden SUT'ler için test uygulamasının builder.ConfigureServices geri çağırması SUT Startup.ConfigureServices kodundan önce yürütülür. Test uygulamasının builder.ConfigureTestServices geri çağırması daha sonra yürütülür.

    Örnek uygulama, veritabanı bağlamı için hizmet tanımlayıcısını bulur ve hizmet kaydını kaldırmak için tanımlayıcıyı kullanır. Ardından fabrika, testler için bellek içi veritabanı kullanan yeni ApplicationDbContext bir veritabanı ekler.

    Bellek içi veritabanından farklı bir veritabanına bağlanmak için, bağlamı UseInMemoryDatabase farklı bir veritabanına bağlamak için çağrıyı değiştirin. SQL Server test veritabanını kullanmak için:

    services.AddDbContext<ApplicationDbContext>((options, context) => 
    {
        context.UseSqlServer(
            Configuration.GetConnectionString("TestingDbConnectionString"));
    });
    
  2. Test sınıflarında özel CustomWebApplicationFactory öğesini kullanın. Aşağıdaki örnekte sınıfındaki IndexPageTests fabrika kullanılır:

    public class IndexPageTests : 
        IClassFixture<CustomWebApplicationFactory<RazorPagesProject.Startup>>
    {
        private readonly HttpClient _client;
        private readonly CustomWebApplicationFactory<RazorPagesProject.Startup> 
            _factory;
    
        public IndexPageTests(
            CustomWebApplicationFactory<RazorPagesProject.Startup> factory)
        {
            _factory = factory;
            _client = factory.CreateClient(new WebApplicationFactoryClientOptions
                {
                    AllowAutoRedirect = false
                });
        }
    

    Örnek uygulamanın istemcisi, aşağıdaki yeniden yönlendirmelerin HttpClient engellenmesi için yapılandırılır. Sahte kimlik doğrulaması bölümünde daha sonra açıklandığı gibi bu, testlerin uygulamanın ilk yanıtının sonucunu denetlemesine izin verir. İlk yanıt, bu testlerin çoğunda üst bilgi içeren bir Location yeniden yönlendirmedir.

  3. Tipik bir test, isteği ve yanıtı işlemek için ve yardımcı yöntemlerini kullanır HttpClient :

    [Fact]
    public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
    {
        // Arrange
        var defaultPage = await _client.GetAsync("/");
        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
    
        //Act
        var response = await _client.SendAsync(
            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
            (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']"));
    
        // Assert
        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
        Assert.Equal("/", response.Headers.Location.OriginalString);
    }
    

SUT'ye yapılan herhangi bir POST isteği, uygulamanın veri koruma önleme sistemi tarafından otomatik olarak yapılan kötü amaçlı yazılımdan koruma denetimini karşılamalıdır. Testin POST isteğini düzenlemek için test uygulamasının şunları yapması gerekir:

  1. Sayfa için bir istekte bulun.
  2. Antiforgery'yi cookie ayrıştırın ve yanıttan doğrulama belirteci isteyin.
  3. POST isteğini sahteciliğe cookie karşı koruma ve istek doğrulama belirteci ile gerçekleştirin.

Örnek SendAsync uygulamadaki GetDocumentAsync yardımcı uzantı yöntemleri (Helpers/HttpClientExtensions.cs) ve yardımcı yöntemi (Helpers/HtmlHelpers.cs), aşağıdaki yöntemlerle antiforgery denetimini işlemek için AngleSharp ayrıştırıcısını kullanır:

  • GetDocumentAsync: alır HttpResponseMessage ve döndürür IHtmlDocument. GetDocumentAsync, özgün HttpResponseMessageöğesini temel alan bir sanal yanıt hazırlayan bir fabrika kullanır. Daha fazla bilgi için AngleSharp belgelerine bakın.
  • SendAsyncoluşturma HttpRequestMessage ve SUT'ye istek gönderme çağrısı SendAsync(HttpRequestMessage) için uzantı yöntemleriHttpClient. HTML formunu (IHtmlFormElement) ve aşağıdakileri kabul etmek için SendAsync aşırı yüklemeler:
    • Formun Gönder düğmesi (IHtmlElement)
    • Form değerleri koleksiyonu (IEnumerable<KeyValuePair<string, string>>)
    • Gönder düğmesi (IHtmlElement) ve form değerleri (IEnumerable<KeyValuePair<string, string>>)

Not

AngleSharp , bu konuda ve örnek uygulamada gösterim amacıyla kullanılan bir üçüncü taraf ayrıştırma kitaplığıdır. AngleSharp, ASP.NET Core uygulamalarının tümleştirme testi için desteklenmez veya gerekli değildir. Html Çeviklik Paketi (HAP) gibi diğer ayrıştırıcılar kullanılabilir. Bir diğer yaklaşım da, sahteciliğe karşı koruma sisteminin istek doğrulama belirtecini ve kötü amaçlı yazılımdan korumayı doğrudan işlemek için kod yazmaktır cookie .

WithWebHostBuilder ile istemciyi özelleştirme

Bir test yönteminde ek yapılandırma gerektiğinde, WithWebHostBuilder yapılandırma tarafından daha da özelleştirilmiş bir ile yeni WebApplicationFactory bir IWebHostBuilder oluşturur.

Post_DeleteMessageHandler_ReturnsRedirectToRoot Örnek uygulamanın test yöntemi, kullanımını WithWebHostBuildergösterir. Bu test, SUT'da form gönderimini tetikleyerek veritabanında bir kayıt silme işlemi gerçekleştirir.

sınıfındaki IndexPageTests başka bir test veritabanındaki tüm kayıtları silen ve yönteminden önce Post_DeleteMessageHandler_ReturnsRedirectToRoot çalıştırabilen bir işlem gerçekleştirdiğinden, SUT'nin silmesi için bir kaydın mevcut olduğundan emin olmak için bu test yönteminde veritabanı yeniden görüntülenir. SUT'daki formun ilk sil düğmesinin messages seçilmesi, SUT isteğinde benzetim yapılır:

[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureServices(services =>
            {
                var serviceProvider = services.BuildServiceProvider();

                using (var scope = serviceProvider.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices
                        .GetRequiredService<ApplicationDbContext>();
                    var logger = scopedServices
                        .GetRequiredService<ILogger<IndexPageTests>>();

                    try
                    {
                        Utilities.ReinitializeDbForTests(db);
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, "An error occurred seeding " +
                            "the database with test messages. Error: {Message}", 
                            ex.Message);
                    }
                }
            });
        })
        .CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
    var defaultPage = await client.GetAsync("/");
    var content = await HtmlHelpers.GetDocumentAsync(defaultPage);

    //Act
    var response = await client.SendAsync(
        (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
        (IHtmlButtonElement)content.QuerySelector("form[id='messages']")
            .QuerySelector("div[class='panel-body']")
            .QuerySelector("button"));

    // Assert
    Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
    Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
    Assert.Equal("/", response.Headers.Location.OriginalString);
}

İstemci seçenekleri

Aşağıdaki tabloda, örnekler oluşturulurken HttpClient kullanılabilen varsayılan WebApplicationFactoryClientOptions değer gösterilmektedir.

Seçenek Açıklama Varsayılan
AllowAutoRedirect Örneklerin yeniden yönlendirme yanıtlarını otomatik olarak izlemesi gerekip gerekmediğini HttpClient alır veya ayarlar. true
BaseAddress Örneklerin temel adresini HttpClient alır veya ayarlar. http://localhost
HandleCookies Örneklerin tanımlama bilgilerini işleyip işlemeyeceğini HttpClient alır veya ayarlar. true
MaxAutomaticRedirections Örneklerin izlemesi gereken en fazla yeniden yönlendirme yanıt HttpClient sayısını alır veya ayarlar. 7

sınıfını WebApplicationFactoryClientOptions oluşturun ve yöntemine CreateClient() geçirin (varsayılan değerler kod örneğinde gösterilir):

// Default client option values are shown
var clientOptions = new WebApplicationFactoryClientOptions();
clientOptions.AllowAutoRedirect = true;
clientOptions.BaseAddress = new Uri("http://localhost");
clientOptions.HandleCookies = true;
clientOptions.MaxAutomaticRedirections = 7;

_client = _factory.CreateClient(clientOptions);

Sahte hizmetler ekleme

Hizmetler, konak oluşturucusunda çağrısı ConfigureTestServices yapılan bir testte geçersiz kılınabilir. Sahte hizmetler eklemek için SUT'nin bir yöntemi olan bir Startup Startup.ConfigureServices sınıfı olmalıdır.

Örnek SUT, teklif döndüren kapsamlı bir hizmet içerir. Teklif, Dizin sayfası istendiğinde Dizin sayfasındaki gizli bir alana eklenir.

Services/IQuoteService.cs:

public interface IQuoteService
{
    Task<string> GenerateQuote();
}

Services/QuoteService.cs:

// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
    public Task<string> GenerateQuote()
    {
        return Task.FromResult<string>(
            "Come on, Sarah. We've an appointment in London, " +
            "and we're already 30,000 years late.");
    }
}

Startup.cs:

services.AddScoped<IQuoteService, QuoteService>();

Pages/Index.cshtml.cs:

public class IndexModel : PageModel
{
    private readonly ApplicationDbContext _db;
    private readonly IQuoteService _quoteService;

    public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
    {
        _db = db;
        _quoteService = quoteService;
    }

    [BindProperty]
    public Message Message { get; set; }

    public IList<Message> Messages { get; private set; }

    [TempData]
    public string MessageAnalysisResult { get; set; }

    public string Quote { get; private set; }

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

        Quote = await _quoteService.GenerateQuote();
    }

Pages/Index.cs:

<input id="quote" type="hidden" value="@Model.Quote">

SUT uygulaması çalıştırıldığında aşağıdaki işaretleme oluşturulur:

<input id="quote" type="hidden" value="Come on, Sarah. We&#x27;ve an appointment in 
    London, and we&#x27;re already 30,000 years late.">

Tümleştirme testinde hizmeti ve teklif eklemeyi test etmek için test tarafından SUT'ye sahte hizmet eklenir. Sahte hizmet, uygulamanın yerine test uygulaması QuoteService tarafından sağlanan ve adlı TestQuoteServicebir hizmettir:

IntegrationTests.IndexPageTests.cs:

// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
    public Task<string> GenerateQuote()
    {
        return Task.FromResult<string>(
            "Something's interfering with time, Mr. Scarman, " +
            "and time is my business.");
    }
}

ConfigureTestServices çağrılır ve kapsamı belirlenmiş hizmet kaydedilir:

[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddScoped<IQuoteService, TestQuoteService>();
            });
        })
        .CreateClient();

    //Act
    var defaultPage = await client.GetAsync("/");
    var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
    var quoteElement = content.QuerySelector("#quote");

    // Assert
    Assert.Equal("Something's interfering with time, Mr. Scarman, " +
        "and time is my business.", quoteElement.Attributes["value"].Value);
}

Testin yürütülmesi sırasında oluşturulan işaretleme, tarafından TestQuoteServicesağlanan tırnak metnini yansıtır, böylece onay geçer:

<input id="quote" type="hidden" value="Something&#x27;s interfering with time, 
    Mr. Scarman, and time is my business.">

Sahte kimlik doğrulaması

Sınıfındaki AuthTests testler güvenli bir uç nokta olup olmadığını denetler:

  • Kimliği doğrulanmamış bir kullanıcıyı uygulamanın Oturum Açma sayfasına yönlendirir.
  • Kimliği doğrulanmış bir kullanıcının içeriğini döndürür.

SUT'de sayfa, /SecurePage sayfaya uygulamak için bir AuthorizePage AuthorizeFilter kural kullanır. Daha fazla bilgi için bkz Razor . Sayfa yetkilendirme kuralları.

services.AddRazorPages(options =>
{
    options.Conventions.AuthorizePage("/SecurePage");
});

Testte Get_SecurePageRedirectsAnUnauthenticatedUser , ayarı WebApplicationFactoryClientOptions olarak ayarlanarak AllowAutoRedirect yeniden yönlendirmelere izin vermemeye falseayarlanmıştır:

[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
    // Arrange
    var client = _factory.CreateClient(
        new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });

    // Act
    var response = await client.GetAsync("/SecurePage");

    // Assert
    Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
    Assert.StartsWith("http://localhost/Identity/Account/Login", 
        response.Headers.Location.OriginalString);
}

İstemcinin yeniden yönlendirmeyi izlemesine izin vermeyerek aşağıdaki denetimler yapılabilir:

  • SUT tarafından döndürülen durum kodu, Oturum Açma sayfasına yönlendirildikten sonraki son durum koduna değil beklenen HttpStatusCode.Redirect sonucta göre denetlenebilir. Bu, olur HttpStatusCode.OK.
  • Yanıt Location üst bilgilerindeki üst bilgi değeri, üst bilginin mevcut olmadığı son Oturum Açma sayfası yanıtıyla değil ile Location başladığını http://localhost/Identity/Account/Loginonaylamak için denetlendi.

Test uygulaması, kimlik doğrulaması ve yetkilendirme özelliklerini test etmek için bir AuthenticationHandler<TOptions> ConfigureTestServices ile dalga geçebilir. En düşük senaryo bir AuthenticateResult.Successdöndürür:

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, 
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
        var identity = new ClaimsIdentity(claims, "Test");
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, "Test");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

TestAuthHandler, kimlik doğrulama düzeni için kaydedildiği yere AddAuthentication ayarlandığında Test kullanıcının kimliğini doğrulamak için ConfigureTestServicesçağrılır. Düzenin uygulamanızın Test beklediği düzenle eşleşmesi önemlidir. Aksi takdirde kimlik doğrulaması çalışmaz.

[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddAuthentication("Test")
                    .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
                        "Test", options => {});
            });
        })
        .CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false,
        });

    client.DefaultRequestHeaders.Authorization = 
        new AuthenticationHeaderValue("Test");

    //Act
    var response = await client.GetAsync("/SecurePage");

    // Assert
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

hakkında WebApplicationFactoryClientOptionsdaha fazla bilgi için İstemci seçenekleri bölümüne bakın.

Ortamı ayarlama

Varsayılan olarak, SUT'nin ana bilgisayar ve uygulama ortamı Geliştirme ortamını kullanacak şekilde yapılandırılır. kullanırken IHostBuilderSUT ortamını geçersiz kılmak için:

  • Ortam değişkenini ASPNETCORE_ENVIRONMENT (örneğin, Staging, Productionveya gibi başka bir özel değer) Testingayarlayın.
  • ön ekli ASPNETCOREortam değişkenlerini okumak için test uygulamasında geçersiz kılınCreateHostBuilder.
protected override IHostBuilder CreateHostBuilder() =>
    base.CreateHostBuilder()
        .ConfigureHostConfiguration(
            config => config.AddEnvironmentVariables("ASPNETCORE"));

SUT Web Konağını ()IWebHostBuilder kullanıyorsa, geçersiz kılın CreateWebHostBuilder:

protected override IWebHostBuilder CreateWebHostBuilder() =>
    base.CreateWebHostBuilder().UseEnvironment("Testing");

Test altyapısı uygulama içeriği kök yolunu nasıl çıkarsar?

Oluşturucu, WebApplicationFactory derlemeye eşit TEntryPoint bir WebApplicationFactoryContentRootAttribute anahtara sahip tümleştirme testlerini içeren derlemede öğesini arayarak uygulama içeriği kök yolunu çıkarsarSystem.Reflection.Assembly.FullName. Doğru anahtara sahip bir özniteliğin bulunamazsa, WebApplicationFactory çözüm dosyasını aramaya (.sln) geri döner ve derleme adını çözüm dizinine ekler TEntryPoint . Uygulama kök dizini (içerik kök yolu), görünümleri ve içerik dosyalarını bulmak için kullanılır.

Gölge kopyalamayı devre dışı bırakma

Gölge kopyalama, testlerin çıkış dizininden farklı bir dizinde yürütülmesine neden olur. Testleriniz ile ilgili Assembly.Location dosyaları yüklemeye bağlıysa ve sorunlarla karşılaşıyorsanız, gölge kopyalamayı devre dışı bırakmanız gerekebilir.

xUnit kullanırken gölge kopyalamayı devre dışı bırakmak için test projesi dizininizde doğru yapılandırma ayarıyla bir xunit.runner.json dosya oluşturun:

{
  "shadowCopy": false
}

Nesnelerin atılması

Uygulamanın testleri IClassFixture yürütüldükten TestServer ve HttpClient xUnit WebApplicationFactorytarafından atıldığında atılır. Geliştirici tarafından örneği oluşturulmuş nesneler elden çıkarılma gerektiriyorsa, bunları IClassFixture uygulamada atın. Daha fazla bilgi için bkz . Dispose yöntemi uygulama.

Tümleştirme testleri örneği

Örnek uygulama iki uygulamadan oluşur:

Uygulama Proje dizini Açıklama
İleti uygulaması (SUT) src/RazorPagesProject Kullanıcının bir ileti eklemesine, silmesine, tümünü silmesine ve iletileri analiz etmesine izin verir.
Test uygulaması tests/RazorPagesProject.Tests SUT'yi tümleştirme 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, dizindeki bir komut isteminde aşağıdaki komutu yürütürtests/RazorPagesProject.Tests:

dotnet test

İleti uygulaması (SUT) kuruluşu

SUT, 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 (ileti başına ortalama sözcükler) denetlemek için bir kullanıcı arabirimi ve sayfa modeli yöntemleri sağlar.
  • 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 AppDbContext () bir veri erişim katmanı (Data/AppDbContext.csDAL) içerir.
  • Uygulama başlangıcında veritabanı boşsa, ileti deposu üç iletiyle başlatılır.
  • Uygulama yalnızca kimliği doğrulanmış bir /SecurePage kullanıcı tarafından erişilebilen bir içerir.

†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.

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 Test denetleyicisi mantığı (örnek, depo düzenini uygular).

Uygulama kuruluşunu test et

Test uygulaması, dizinin içindeki tests/RazorPagesProject.Tests bir konsol uygulamasıdır.

Uygulama dizinini test et Açıklama
AuthTests Aşağıdakiler için test yöntemlerini içerir:
  • Kimliği doğrulanmamış bir kullanıcı tarafından güvenli bir sayfaya erişme.
  • Kimliği doğrulanmış bir kullanıcı tarafından sahtesiyle AuthenticationHandler<TOptions>güvenli bir sayfaya erişme.
  • GitHub kullanıcı profili alma ve profilin kullanıcı oturum açma bilgilerini denetleme.
BasicTests Yönlendirme ve içerik türü için bir test yöntemi içerir.
IntegrationTests Özel WebApplicationFactory sınıf kullanarak Dizin sayfasının tümleştirme testlerini içerir.
Helpers/Utilities
  • Utilities.csInitializeDbForTests test verileriyle veritabanının tohumunu oluşturmak için kullanılan yöntemi içerir.
  • HtmlHelpers.cs test yöntemleri tarafından kullanılmak üzere AngleSharp IHtmlDocument döndürmek için bir yöntem sağlar.
  • HttpClientExtensions.cs SUT'ye istek göndermek için için SendAsync aşırı yüklemeler sağlar.

Test çerçevesi xUnit'tir. Tümleştirme testleri, öğesini içeren TestServerkullanılarak Microsoft.AspNetCore.TestHostyapılır. Microsoft.AspNetCore.Mvc.Testing Paket test ana bilgisayarını ve test sunucusunu yapılandırmak için kullanıldığından TestHost ve TestServer paketleri, test uygulamasının proje dosyasında veya test uygulamasındaki geliştirici yapılandırmasında doğrudan paket başvuruları gerektirmez.

Tümleştirme testleri genellikle test yürütmeden önce veritabanında küçük bir veri kümesi gerektirir. Örneğin, silme testi veritabanı kaydı silme işlemini çağırır, bu nedenle silme isteğinin başarılı olması için veritabanında en az bir kayıt olmalıdır.

Örnek uygulama, testleri yürütürken kullanabileceği üç iletiyle Utilities.cs veritabanının çekirdeğini oluşturur:

public static void InitializeDbForTests(ApplicationDbContext db)
{
    db.Messages.AddRange(GetSeedingMessages());
    db.SaveChanges();
}

public static void ReinitializeDbForTests(ApplicationDbContext db)
{
    db.Messages.RemoveRange(db.Messages);
    InitializeDbForTests(db);
}

public static List<Message> GetSeedingMessages()
{
    return new List<Message>()
    {
        new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
        new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
        new Message(){ Text = "TEST RECORD: To the rational mind, " +
            "nothing is inexplicable; only unexplained." }
    };
}

SUT'nin veritabanı bağlamı yöntemine Startup.ConfigureServices kaydedilir. Test uygulamasının builder.ConfigureServices geri çağırması, uygulamanın Startup.ConfigureServices kodu yürütüldükten sonra yürütülür. Testlerde farklı bir veritabanı kullanmak için uygulamanın veritabanı bağlamı içinde builder.ConfigureServicesdeğiştirilmelidir. Daha fazla bilgi için WebApplicationFactory'yi Özelleştirme bölümüne bakın.

Web Konağını kullanmaya devam eden SUT'ler için test uygulamasının builder.ConfigureServices geri çağırması SUT Startup.ConfigureServices kodundan önce yürütülür. Test uygulamasının builder.ConfigureTestServices geri çağırması daha sonra yürütülür.

Ek kaynaklar

Bu makalede, birim testleri hakkında temel bilgiler olduğu varsayılır. Test kavramlarını bilmiyorsanız .NET Core ve .NET Standard'da Birim Testi makalesine ve bağlantılı içeriğine bakın.

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

Örnek uygulama bir Razor Sayfalar uygulamasıdır ve Sayfalar hakkında temel bir anlayış olduğunu Razor varsayar. Pages'ı tanımıyorsanız Razor aşağıdaki makalelere bakın:

SPA'ları test etme amacıyla tarayıcıyı otomatikleştirebilen .NET için Playwright gibi bir araç öneririz.

Tümleştirme testlerine giriş

Tümleştirme testleri, bir uygulamanın bileşenlerini birim testlerinden daha geniş bir düzeyde değerlendirir. Birim testleri, tek tek sınıf yöntemleri gibi yalıtılmış yazılım bileşenlerini test etmek için kullanılır. Tümleştirme testleri, iki veya daha fazla uygulama bileşeninin birlikte çalışarak beklenen bir sonuç elde ettiğini ve büyük olasılıkla bir isteği tam olarak işlemek için gereken her bileşenin de dahil olduğunu onaylar.

Bu daha geniş testler, genellikle aşağıdaki bileşenler de dahil olmak üzere uygulamanın altyapısını ve tüm çerçevesini test etmek için kullanılır:

  • Veritabanı
  • Dosya sistemi
  • Ağ gereçleri
  • İstek-yanıt işlem hattı

Birim testleri, altyapı bileşenleri yerine sahte veya sahte nesneler olarak bilinen fabrikatlanmış bileşenleri kullanır.

Birim testlerinin aksine tümleştirme testleri:

  • Uygulamanın üretimde kullandığı gerçek bileşenleri kullanın.
  • Daha fazla kod ve veri işleme gerektirir.
  • Çalıştırılması daha uzun sürer.

Bu nedenle, tümleştirme testlerinin kullanımını en önemli altyapı senaryolarıyla sınırlayın. Bir davranış birim testi veya tümleştirme testi kullanılarak test edilebiliyorsa birim testini seçin.

Tümleştirme testlerinin tartışmalarında, test edilen projeye genellikle Test Altındaki Sistem veya kısaca "SUT" adı verilir. "SUT" bu makalede test edilen ASP.NET Core uygulamasına başvurmak için kullanılır.

Veritabanları ve dosya sistemleriyle verilerin ve dosya erişiminin her permütasyonu için tümleştirme testleri yazmayın. Bir uygulama genelinde veritabanları ve dosya sistemleriyle kaç yerde etkileşim kurduğundan bağımsız olarak, odaklanmış bir dizi okuma, yazma, güncelleştirme ve silme tümleştirme testi genellikle veritabanı ve dosya sistemi bileşenlerini yeterince test edebilmektedir. Bu bileşenlerle etkileşim kuran yöntem mantığının rutin testleri için birim testlerini kullanın. Birim testlerinde, altyapı sahteleri veya sahteleri kullanımı test yürütmenin daha hızlı gerçekleşmesine neden olur.

ASP.NET Core tümleştirme testleri

ASP.NET Core'daki tümleştirme testleri aşağıdakileri gerektirir:

  • Testleri içermek ve yürütmek için bir test projesi kullanılır. Test projesinin SUT başvurusu vardır.
  • Test projesi SUT için bir test web konağı oluşturur ve SUT ile istekleri ve yanıtları işlemek için bir test sunucusu istemcisi kullanır.
  • Testleri yürütmek ve test sonuçlarını raporlamak için bir test çalıştırıcısı kullanılır.

Tümleştirme testleri, her zamanki Düzenleme, Eylem ve Onay testi adımlarını içeren bir dizi olayı izler:

  1. SUT'nin web konağı yapılandırıldı.
  2. Uygulamaya istek göndermek için bir test sunucusu istemcisi oluşturulur.
  3. Test düzenleme adımı yürütülür: Test uygulaması bir istek hazırlar.
  4. Eylem test adımı yürütülür: İstemci isteği gönderir ve yanıtı alır.
  5. Assert test adımı yürütülür: Gerçek yanıt, beklenen yanıta göre geçiş veya başarısız olarak doğrulanır.
  6. Tüm testler yürütülene kadar işlem devam eder.
  7. Test sonuçları bildirilir.

Genellikle test web konağı, test çalıştırmaları için uygulamanın normal web ana bilgisayarından farklı yapılandırılır. Örneğin, testler için farklı bir veritabanı veya farklı uygulama ayarları kullanılabilir.

Test web konağı ve bellek içi test sunucusu ()TestServer gibi altyapı bileşenleri Microsoft.AspNetCore.Mvc.Testing paketi tarafından sağlanır veya yönetilir. Bu paketin kullanımı test oluşturma ve yürütmeyi kolaylaştırır.

Paket Microsoft.AspNetCore.Mvc.Testing aşağıdaki görevleri işler:

Birim testleri belgelerinde bir test projesi ve test çalıştırıcısının nasıl ayarlanacağı ve testleri çalıştırma hakkında ayrıntılı yönergelerin yanı sıra testleri ve test sınıflarını adlandırmaya yönelik öneriler açıklanmaktadır.

Birim testlerini tümleştirme testlerinden farklı projelere ayırın. Testleri ayırma:

  • Altyapı testi bileşenlerinin birim testlerine yanlışlıkla dahil edilmesini sağlamaya yardımcı olur.
  • Hangi test kümesinin çalıştırıldığı üzerinde denetime izin verir.

Sayfalar uygulamalarının ve MVC uygulamalarının testlerinin Razor yapılandırması arasında neredeyse hiçbir fark yoktur. Tek fark, testlerin nasıl adlandırıldığındandır. Razor Sayfalar uygulamasında, sayfa uç noktalarının testleri genellikle sayfa modeli sınıfından (örneğin, IndexPageTests Dizin sayfası için bileşen tümleştirmesini test etmek için) adlandırılır. MVC uygulamasında testler genellikle denetleyici sınıflarına göre düzenlenir ve test ettikleri denetleyicilerden (örneğin, HomeControllerTests denetleyici için bileşen tümleştirmesini Home test etmek için) adlandırılır.

Uygulama önkoşullarını test edin

Test projesinin aşağıdakileri yapması gerekir:

Bu önkoşullar örnek uygulamada görülebilir. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Dosyayı inceleyin. Örnek uygulama xUnit test çerçevesini ve AngleSharp ayrıştırıcı kitaplığını kullandığından örnek uygulama da aşağıdakilere başvurur:

Sürüm 2.4.2 veya üzerini kullanan xunit.runner.visualstudio uygulamalarda test projesinin pakete başvurması Microsoft.NET.Test.Sdk gerekir.

Entity Framework Core, testlerde de kullanılır. GitHub'daki proje dosyasına bakın.

SUT ortamı

SUT'nin ortamı ayarlı değilse, ortam varsayılan olarak Geliştirme olarak ayarlanır.

Varsayılan WebApplicationFactory ile temel testler

Aşağıdakilerden birini yaparak örtük olarak tanımlanmış Program sınıfı test projesinde kullanıma sunma:

  • Web uygulamasından test projesine iç türleri kullanıma sunma. Bu işlem SUT projesinin dosyasında (.csproj):

    <ItemGroup>
         <InternalsVisibleTo Include="MyTestProject" />
    </ItemGroup>
    
  • Program Kısmi bir sınıf bildirimi kullanarak sınıfını genel yapın:

    var builder = WebApplication.CreateBuilder(args);
    // ... Configure services, routes, etc.
    app.Run();
    + public partial class Program { }
    

    Örnek uygulama kısmi sınıf yaklaşımını Program kullanır.

WebApplicationFactory<TEntryPoint> tümleştirme testlerine yönelik bir TestServer oluşturmak için kullanılır. TEntryPoint genellikle Program.csSUT'nin giriş noktası sınıfıdır.

Test sınıfları, sınıfın testleri içerdiğini belirtmek ve sınıftaki testler arasında paylaşılan nesne örnekleri sağlamak için bir sınıf fikstür arabirimi (IClassFixture) uygular.

Aşağıdaki test sınıfı, BasicTestsSUT'yi önyüklemek ve bir test yöntemi Get_EndpointsReturnSuccessAndCorrectContentTypesağlamak HttpClient için öğesini kullanırWebApplicationFactory. yöntemi, yanıt durum kodunun başarılı olduğunu (200-299) ve Content-Type üst bilgi de birkaç uygulama sayfası için doğrular text/html; charset=utf-8 .

CreateClient() otomatik olarak yeniden yönlendirmeleri izleyen ve tanımlama bilgilerini işleyen bir örneği HttpClient oluşturur.

public class BasicTests 
    : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public BasicTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }

    [Theory]
    [InlineData("/")]
    [InlineData("/Index")]
    [InlineData("/About")]
    [InlineData("/Privacy")]
    [InlineData("/Contact")]
    public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
    {
        // Arrange
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync(url);

        // Assert
        response.EnsureSuccessStatusCode(); // Status Code 200-299
        Assert.Equal("text/html; charset=utf-8", 
            response.Content.Headers.ContentType.ToString());
    }
}

Varsayılan olarak, Genel Veri Koruma Yönetmeliği onay ilkesi etkinleştirildiğinde gerekli olmayan tanımlama bilgileri istekler arasında korunmaz. TempData sağlayıcısı tarafından kullanılanlar gibi temel olmayan tanımlama bilgilerini korumak için bunları testlerinizde temel olarak işaretleyin. Temel olarak işaretleme cookie yönergeleri için bkz . Temel tanımlama bilgileri.

AngleSharp ile Application Parts antiforgery denetimleri karşılaştırması

Bu makalede, sayfaları yükleyerek ve HTML ayrıştırarak antiforgery denetimlerini işlemek için AngleSharp ayrıştırıcısı kullanılır. Denetleyici ve Razor Sayfalar görünümlerinin uç noktalarını tarayıcıda nasıl işlediklerine önem vermeden daha düşük bir düzeyde test için kullanmayı Application Partsgöz önünde bulundurun. Uygulama Bölümleri yaklaşımı, uygulamaya gerekli değerleri almak için JSON isteklerinde bulunmak için kullanılabilecek bir denetleyici veya Razor Sayfa ekler. Daha fazla bilgi için Bkz. Tümleştirme Testi ASP.NET Uygulama Bölümlerini Kullanarak Sahtekarlık Önleme ile Korunan Temel Kaynaklar ve Martin Costello tarafından ilişkili GitHub deposu.

WebApplicationFactory'i özelleştirme

Web ana bilgisayar yapılandırması, bir veya daha fazla özel fabrika oluşturmak için öğesinden devralınarak test sınıflarından WebApplicationFactory<TEntryPoint> bağımsız olarak oluşturulabilir:

  1. öğesinden WebApplicationFactory devralın ve öğesini geçersiz kılın ConfigureWebHost. ile IWebHostBuilder hizmet koleksiyonunun yapılandırılmasına izin verir IWebHostBuilder.ConfigureServices

    public class CustomWebApplicationFactory<TProgram>
        : WebApplicationFactory<TProgram> where TProgram : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                var dbContextDescriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbContextOptions<ApplicationDbContext>));
    
                services.Remove(dbContextDescriptor);
    
                var dbConnectionDescriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbConnection));
    
                services.Remove(dbConnectionDescriptor);
    
                // Create open SqliteConnection so EF won't automatically close it.
                services.AddSingleton<DbConnection>(container =>
                {
                    var connection = new SqliteConnection("DataSource=:memory:");
                    connection.Open();
    
                    return connection;
                });
    
                services.AddDbContext<ApplicationDbContext>((container, options) =>
                {
                    var connection = container.GetRequiredService<DbConnection>();
                    options.UseSqlite(connection);
                });
            });
    
            builder.UseEnvironment("Development");
        }
    }
    

    Örnek uygulamadaki veritabanı tohumlama yöntemi tarafından InitializeDbForTests gerçekleştirilir. Yöntemi, Tümleştirme testleri örneği: Test uygulaması kuruluşu bölümünde açıklanmıştır.

    SUT'nin veritabanı bağlamı içinde Program.cskaydedilir. Test uygulamasının builder.ConfigureServices geri çağırması, uygulamanın Program.cs kodu yürütüldükten sonra yürütülür. Testlerde uygulamanın veritabanından farklı bir veritabanı kullanmak için, uygulamanın veritabanı bağlamı içinde builder.ConfigureServicesdeğiştirilmelidir.

    Örnek uygulama, veritabanı bağlamı için hizmet tanımlayıcısını bulur ve hizmet kaydını kaldırmak için tanımlayıcıyı kullanır. Ardından fabrika, testler için bellek içi veritabanı kullanan yeni ApplicationDbContext bir ekliyor..

    Farklı bir veritabanına bağlanmak için öğesini DbConnectiondeğiştirin. SQL Server test veritabanını kullanmak için:

  1. Test sınıflarında özel CustomWebApplicationFactory öğesini kullanın. Aşağıdaki örnekte sınıfındaki IndexPageTests fabrika kullanılır:

    public class IndexPageTests :
        IClassFixture<CustomWebApplicationFactory<Program>>
    {
        private readonly HttpClient _client;
        private readonly CustomWebApplicationFactory<Program>
            _factory;
    
        public IndexPageTests(
            CustomWebApplicationFactory<Program> factory)
        {
            _factory = factory;
            _client = factory.CreateClient(new WebApplicationFactoryClientOptions
            {
                AllowAutoRedirect = false
            });
        }
    

    Örnek uygulamanın istemcisi, aşağıdaki yeniden yönlendirmelerin HttpClient engellenmesi için yapılandırılır. Sahte kimlik doğrulaması bölümünde daha sonra açıklandığı gibi bu, testlerin uygulamanın ilk yanıtının sonucunu denetlemesine izin verir. İlk yanıt, bu testlerin çoğunda üst bilgi içeren bir Location yeniden yönlendirmedir.

  2. Tipik bir test, isteği ve yanıtı işlemek için ve yardımcı yöntemlerini kullanır HttpClient :

    [Fact]
    public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot()
    {
        // Arrange
        var defaultPage = await _client.GetAsync("/");
        var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
    
        //Act
        var response = await _client.SendAsync(
            (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
            (IHtmlButtonElement)content.QuerySelector("button[id='deleteAllBtn']"));
    
        // Assert
        Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
        Assert.Equal("/", response.Headers.Location.OriginalString);
    }
    

SUT'ye yapılan herhangi bir POST isteği, uygulamanın veri koruma önleme sistemi tarafından otomatik olarak yapılan kötü amaçlı yazılımdan koruma denetimini karşılamalıdır. Testin POST isteğini düzenlemek için test uygulamasının şunları yapması gerekir:

  1. Sayfa için bir istekte bulun.
  2. Antiforgery'yi cookie ayrıştırın ve yanıttan doğrulama belirteci isteyin.
  3. POST isteğini sahteciliğe cookie karşı koruma ve istek doğrulama belirteci ile gerçekleştirin.

Örnek SendAsync uygulamadaki GetDocumentAsync yardımcı uzantı yöntemleri (Helpers/HttpClientExtensions.cs) ve yardımcı yöntemi (Helpers/HtmlHelpers.cs), aşağıdaki yöntemlerle antiforgery denetimini işlemek için AngleSharp ayrıştırıcısını kullanır:

  • GetDocumentAsync: alır HttpResponseMessage ve döndürür IHtmlDocument. GetDocumentAsync, özgün HttpResponseMessageöğesini temel alan bir sanal yanıt hazırlayan bir fabrika kullanır. Daha fazla bilgi için AngleSharp belgelerine bakın.
  • SendAsyncoluşturma HttpRequestMessage ve SUT'ye istek gönderme çağrısı SendAsync(HttpRequestMessage) için uzantı yöntemleriHttpClient. HTML formunu (IHtmlFormElement) ve aşağıdakileri kabul etmek için SendAsync aşırı yüklemeler:
    • Formun Gönder düğmesi (IHtmlElement)
    • Form değerleri koleksiyonu (IEnumerable<KeyValuePair<string, string>>)
    • Gönder düğmesi (IHtmlElement) ve form değerleri (IEnumerable<KeyValuePair<string, string>>)

AngleSharp, bu makalede ve örnek uygulamada gösterim amacıyla kullanılan bir üçüncü taraf ayrıştırma kitaplığıdır. AngleSharp, ASP.NET Core uygulamalarının tümleştirme testi için desteklenmez veya gerekli değildir. Html Çeviklik Paketi (HAP) gibi diğer ayrıştırıcılar kullanılabilir. Bir diğer yaklaşım da, sahteciliğe karşı koruma sisteminin istek doğrulama belirtecini ve kötü amaçlı yazılımdan korumayı doğrudan işlemek için kod yazmaktır cookie . Daha fazla bilgi için bu makaledeki AngleSharp vs Application Parts antiforgery denetimleri konusuna bakın.

EF-Core bellek içi veritabanı sağlayıcısı sınırlı ve temel test için kullanılabilir, ancak bellek içi test için önerilen seçenek SQLite sağlayıcısıdır.

Bkz . Başlangıç'ı başlangıç filtreleri ile genişletme. Bu, bir test özel hizmet veya ara yazılım gerektirdiğinde yararlı olan kullanarak IStartupFilterara yazılımı yapılandırmayı gösterir.

WithWebHostBuilder ile istemciyi özelleştirme

Bir test yönteminde ek yapılandırma gerektiğinde, WithWebHostBuilder yapılandırma tarafından daha da özelleştirilmiş bir ile yeni WebApplicationFactory bir IWebHostBuilder oluşturur.

Örnek kod, yapılandırılmış hizmetleri test saptamalarıyla değiştirmek için çağırırWithWebHostBuilder. Daha fazla bilgi ve örnek kullanım için bu makaledeki Sahte hizmetler ekleme bölümüne bakın.

Post_DeleteMessageHandler_ReturnsRedirectToRoot Örnek uygulamanın test yöntemi, kullanımını WithWebHostBuildergösterir. Bu test, SUT'da form gönderimini tetikleyerek veritabanında bir kayıt silme işlemi gerçekleştirir.

sınıfındaki IndexPageTests başka bir test veritabanındaki tüm kayıtları silen ve yönteminden önce Post_DeleteMessageHandler_ReturnsRedirectToRoot çalıştırabilen bir işlem gerçekleştirdiğinden, SUT'nin silmesi için bir kaydın mevcut olduğundan emin olmak için bu test yönteminde veritabanı yeniden görüntülenir. SUT'daki formun ilk sil düğmesinin messages seçilmesi, SUT isteğinde benzetim yapılır:

[Fact]
public async Task Post_DeleteMessageHandler_ReturnsRedirectToRoot()
{
    // Arrange
    using (var scope = _factory.Services.CreateScope())
    {
        var scopedServices = scope.ServiceProvider;
        var db = scopedServices.GetRequiredService<ApplicationDbContext>();

        Utilities.ReinitializeDbForTests(db);
    }

    var defaultPage = await _client.GetAsync("/");
    var content = await HtmlHelpers.GetDocumentAsync(defaultPage);

    //Act
    var response = await _client.SendAsync(
        (IHtmlFormElement)content.QuerySelector("form[id='messages']"),
        (IHtmlButtonElement)content.QuerySelector("form[id='messages']")
            .QuerySelector("div[class='panel-body']")
            .QuerySelector("button"));

    // Assert
    Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode);
    Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
    Assert.Equal("/", response.Headers.Location.OriginalString);
}

İstemci seçenekleri

WebApplicationFactoryClientOptions Örnek oluştururken HttpClient varsayılanlar ve kullanılabilir seçenekler için sayfaya bakın.

sınıfını WebApplicationFactoryClientOptions oluşturun ve yöntemine CreateClient() geçirin:

public class IndexPageTests :
    IClassFixture<CustomWebApplicationFactory<Program>>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory<Program>
        _factory;

    public IndexPageTests(
        CustomWebApplicationFactory<Program> factory)
    {
        _factory = factory;
        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
    }

NOT: HTTPS Yeniden Yönlendirme Ara Yazılımı kullanılırken günlüklerde HTTPS yeniden yönlendirme uyarılarından kaçınmak için BaseAddress = new Uri("https://localhost")

Sahte hizmetler ekleme

Hizmetler, konak oluşturucusunda çağrısı ConfigureTestServices yapılan bir testte geçersiz kılınabilir. Geçersiz kılınan hizmetlerin kapsamını testin kendisi olarak tanımlamak için yöntemi bir WithWebHostBuilder konak oluşturucu almak için kullanılır. Bu durum aşağıdaki testlerde görülebilir:

Örnek SUT, teklif döndüren kapsamlı bir hizmet içerir. Teklif, Dizin sayfası istendiğinde Dizin sayfasındaki gizli bir alana eklenir.

Services/IQuoteService.cs:

public interface IQuoteService
{
    Task<string> GenerateQuote();
}

Services/QuoteService.cs:

// Quote ©1975 BBC: The Doctor (Tom Baker); Dr. Who: Planet of Evil
// https://www.bbc.co.uk/programmes/p00pyrx6
public class QuoteService : IQuoteService
{
    public Task<string> GenerateQuote()
    {
        return Task.FromResult<string>(
            "Come on, Sarah. We've an appointment in London, " +
            "and we're already 30,000 years late.");
    }
}

Program.cs:

services.AddScoped<IQuoteService, QuoteService>();

Pages/Index.cshtml.cs:

public class IndexModel : PageModel
{
    private readonly ApplicationDbContext _db;
    private readonly IQuoteService _quoteService;

    public IndexModel(ApplicationDbContext db, IQuoteService quoteService)
    {
        _db = db;
        _quoteService = quoteService;
    }

    [BindProperty]
    public Message Message { get; set; }

    public IList<Message> Messages { get; private set; }

    [TempData]
    public string MessageAnalysisResult { get; set; }

    public string Quote { get; private set; }

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

        Quote = await _quoteService.GenerateQuote();
    }

Pages/Index.cs:

<input id="quote" type="hidden" value="@Model.Quote">

SUT uygulaması çalıştırıldığında aşağıdaki işaretleme oluşturulur:

<input id="quote" type="hidden" value="Come on, Sarah. We&#x27;ve an appointment in 
    London, and we&#x27;re already 30,000 years late.">

Tümleştirme testinde hizmeti ve teklif eklemeyi test etmek için test tarafından SUT'ye sahte hizmet eklenir. Sahte hizmet, uygulamanın yerine test uygulaması QuoteService tarafından sağlanan ve adlı TestQuoteServicebir hizmettir:

IntegrationTests.IndexPageTests.cs:

// Quote ©1975 BBC: The Doctor (Tom Baker); Pyramids of Mars
// https://www.bbc.co.uk/programmes/p00pys55
public class TestQuoteService : IQuoteService
{
    public Task<string> GenerateQuote()
    {
        return Task.FromResult(
            "Something's interfering with time, Mr. Scarman, " +
            "and time is my business.");
    }
}

ConfigureTestServices çağrılır ve kapsamı belirlenmiş hizmet kaydedilir:

[Fact]
public async Task Get_QuoteService_ProvidesQuoteInPage()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddScoped<IQuoteService, TestQuoteService>();
            });
        })
        .CreateClient();

    //Act
    var defaultPage = await client.GetAsync("/");
    var content = await HtmlHelpers.GetDocumentAsync(defaultPage);
    var quoteElement = content.QuerySelector("#quote");

    // Assert
    Assert.Equal("Something's interfering with time, Mr. Scarman, " +
        "and time is my business.", quoteElement.Attributes["value"].Value);
}

Testin yürütülmesi sırasında oluşturulan işaretleme, tarafından TestQuoteServicesağlanan tırnak metnini yansıtır, böylece onay geçer:

<input id="quote" type="hidden" value="Something&#x27;s interfering with time, 
    Mr. Scarman, and time is my business.">

Sahte kimlik doğrulaması

Sınıfındaki AuthTests testler güvenli bir uç nokta olup olmadığını denetler:

  • Kimliği doğrulanmamış bir kullanıcıyı uygulamanın oturum açma sayfasına yönlendirir.
  • Kimliği doğrulanmış bir kullanıcının içeriğini döndürür.

SUT'de sayfa, /SecurePage sayfaya uygulamak için bir AuthorizePage AuthorizeFilter kural kullanır. Daha fazla bilgi için bkz Razor . Sayfa yetkilendirme kuralları.

services.AddRazorPages(options =>
{
    options.Conventions.AuthorizePage("/SecurePage");
});

Testte Get_SecurePageRedirectsAnUnauthenticatedUser , ayarı WebApplicationFactoryClientOptions olarak ayarlanarak AllowAutoRedirect yeniden yönlendirmelere izin vermemeye falseayarlanmıştır:

[Fact]
public async Task Get_SecurePageRedirectsAnUnauthenticatedUser()
{
    // Arrange
    var client = _factory.CreateClient(
        new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });

    // Act
    var response = await client.GetAsync("/SecurePage");

    // Assert
    Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
    Assert.StartsWith("http://localhost/Identity/Account/Login",
        response.Headers.Location.OriginalString);
}

İstemcinin yeniden yönlendirmeyi izlemesine izin vermeyerek aşağıdaki denetimler yapılabilir:

  • SUT tarafından döndürülen durum kodu, oturum açma sayfasına yeniden yönlendirmeden sonraki son durum koduna değil beklenen HttpStatusCode.Redirect sonucta göre denetlenebilir. Bu, olacaktır HttpStatusCode.OK.
  • Yanıt Location üst bilgilerindeki üst bilgi değeri, üst bilginin bulunmayacağı son oturum açma sayfası yanıtıyla değil ile Location başladığını http://localhost/Identity/Account/Loginonaylamak için denetlendi.

Test uygulaması, kimlik doğrulaması ve yetkilendirme özelliklerini test etmek için bir AuthenticationHandler<TOptions> ConfigureTestServices ile dalga geçebilir. En düşük senaryo bir AuthenticateResult.Successdöndürür:

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
        var identity = new ClaimsIdentity(claims, "Test");
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, "TestScheme");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

TestAuthHandler, kimlik doğrulama düzeni için kaydedildiği yere AddAuthentication ayarlandığında TestScheme kullanıcının kimliğini doğrulamak için ConfigureTestServicesçağrılır. Düzenin uygulamanızın TestScheme beklediği düzenle eşleşmesi önemlidir. Aksi takdirde kimlik doğrulaması çalışmaz.

[Fact]
public async Task Get_SecurePageIsReturnedForAnAuthenticatedUser()
{
    // Arrange
    var client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddAuthentication(defaultScheme: "TestScheme")
                    .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
                        "TestScheme", options => { });
            });
        })
        .CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false,
        });

    client.DefaultRequestHeaders.Authorization =
        new AuthenticationHeaderValue(scheme: "TestScheme");

    //Act
    var response = await client.GetAsync("/SecurePage");

    // Assert
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

hakkında WebApplicationFactoryClientOptionsdaha fazla bilgi için İstemci seçenekleri bölümüne bakın.

Kimlik doğrulaması ara yazılımı için temel testler

Kimlik doğrulama ara yazılımının temel testleri için bu GitHub deposuna bakın. Test senaryosuna özgü bir test sunucusu içerir.

Ortamı ayarlama

Ortamı özel uygulama fabrikasında ayarlayın:

public class CustomWebApplicationFactory<TProgram>
    : WebApplicationFactory<TProgram> where TProgram : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var dbContextDescriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbContextOptions<ApplicationDbContext>));

            services.Remove(dbContextDescriptor);

            var dbConnectionDescriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbConnection));

            services.Remove(dbConnectionDescriptor);

            // Create open SqliteConnection so EF won't automatically close it.
            services.AddSingleton<DbConnection>(container =>
            {
                var connection = new SqliteConnection("DataSource=:memory:");
                connection.Open();

                return connection;
            });

            services.AddDbContext<ApplicationDbContext>((container, options) =>
            {
                var connection = container.GetRequiredService<DbConnection>();
                options.UseSqlite(connection);
            });
        });

        builder.UseEnvironment("Development");
    }
}

Test altyapısı uygulama içeriği kök yolunu nasıl çıkarsar?

Oluşturucu, WebApplicationFactory derlemeye eşit TEntryPoint bir WebApplicationFactoryContentRootAttribute anahtara sahip tümleştirme testlerini içeren derlemede öğesini arayarak uygulama içeriği kök yolunu çıkarsarSystem.Reflection.Assembly.FullName. Doğru anahtara sahip bir özniteliğin bulunamazsa, WebApplicationFactory çözüm dosyasını aramaya (.sln) geri döner ve derleme adını çözüm dizinine ekler TEntryPoint . Uygulama kök dizini (içerik kök yolu), görünümleri ve içerik dosyalarını bulmak için kullanılır.

Gölge kopyalamayı devre dışı bırakma

Gölge kopyalama, testlerin çıkış dizininden farklı bir dizinde yürütülmesine neden olur. Testleriniz ile ilgili Assembly.Location dosyaları yüklemeye bağlıysa ve sorunlarla karşılaşıyorsanız, gölge kopyalamayı devre dışı bırakmanız gerekebilir.

xUnit kullanırken gölge kopyalamayı devre dışı bırakmak için test projesi dizininizde doğru yapılandırma ayarıyla bir xunit.runner.json dosya oluşturun:

{
  "shadowCopy": false
}

Nesnelerin atılması

Uygulamanın testleri IClassFixture yürütüldükten TestServer ve HttpClient xUnit WebApplicationFactorytarafından atıldığında atılır. Geliştirici tarafından örneği oluşturulmuş nesneler elden çıkarılma gerektiriyorsa, bunları IClassFixture uygulamada atın. Daha fazla bilgi için bkz . Dispose yöntemi uygulama.

Tümleştirme testleri örneği

Örnek uygulama iki uygulamadan oluşur:

Uygulama Proje dizini Açıklama
İleti uygulaması (SUT) src/RazorPagesProject Kullanıcının bir ileti eklemesine, silmesine, tümünü silmesine ve iletileri analiz etmesine izin verir.
Test uygulaması tests/RazorPagesProject.Tests SUT'yi tümleştirme 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, dizindeki bir komut isteminde aşağıdaki komutu yürütürtests/RazorPagesProject.Tests:

dotnet test

İleti uygulaması (SUT) kuruluşu

SUT, 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 (ileti başına ortalama sözcükler) denetlemek için bir kullanıcı arabirimi ve sayfa modeli yöntemleri sağlar.
  • 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 AppDbContext () bir veri erişim katmanı (Data/AppDbContext.csDAL) içerir.
  • Uygulama başlangıcında veritabanı boşsa, ileti deposu üç iletiyle başlatılır.
  • Uygulama yalnızca kimliği doğrulanmış bir /SecurePage kullanıcı tarafından erişilebilen bir içerir.

† InMemory ile test etme ef makalesi, 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.

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 Test denetleyicisi mantığı (örnek, depo düzenini uygular).

Uygulama kuruluşunu test et

Test uygulaması, dizinin içindeki tests/RazorPagesProject.Tests bir konsol uygulamasıdır.

Uygulama dizinini test et Açıklama
AuthTests Aşağıdakiler için test yöntemlerini içerir:
  • Kimliği doğrulanmamış bir kullanıcı tarafından güvenli bir sayfaya erişme.
  • Kimliği doğrulanmış bir kullanıcı tarafından sahtesiyle AuthenticationHandler<TOptions>güvenli bir sayfaya erişme.
  • GitHub kullanıcı profili alma ve profilin kullanıcı oturum açma bilgilerini denetleme.
BasicTests Yönlendirme ve içerik türü için bir test yöntemi içerir.
IntegrationTests Özel WebApplicationFactory sınıf kullanarak Dizin sayfasının tümleştirme testlerini içerir.
Helpers/Utilities
  • Utilities.csInitializeDbForTests test verileriyle veritabanının tohumunu oluşturmak için kullanılan yöntemi içerir.
  • HtmlHelpers.cs test yöntemleri tarafından kullanılmak üzere AngleSharp IHtmlDocument döndürmek için bir yöntem sağlar.
  • HttpClientExtensions.cs SUT'ye istek göndermek için için SendAsync aşırı yüklemeler sağlar.

Test çerçevesi xUnit'tir. Tümleştirme testleri, öğesini içeren TestServerkullanılarak Microsoft.AspNetCore.TestHostyapılır. Microsoft.AspNetCore.Mvc.Testing Paket test ana bilgisayarını ve test sunucusunu yapılandırmak için kullanıldığından TestHost ve TestServer paketleri, test uygulamasının proje dosyasında veya test uygulamasındaki geliştirici yapılandırmasında doğrudan paket başvuruları gerektirmez.

Tümleştirme testleri genellikle test yürütmeden önce veritabanında küçük bir veri kümesi gerektirir. Örneğin, silme testi veritabanı kaydı silme işlemini çağırır, bu nedenle silme isteğinin başarılı olması için veritabanında en az bir kayıt olmalıdır.

Örnek uygulama, testleri yürütürken kullanabileceği üç iletiyle Utilities.cs veritabanının çekirdeğini oluşturur:

public static void InitializeDbForTests(ApplicationDbContext db)
{
    db.Messages.AddRange(GetSeedingMessages());
    db.SaveChanges();
}

public static void ReinitializeDbForTests(ApplicationDbContext db)
{
    db.Messages.RemoveRange(db.Messages);
    InitializeDbForTests(db);
}

public static List<Message> GetSeedingMessages()
{
    return new List<Message>()
    {
        new Message(){ Text = "TEST RECORD: You're standing on my scarf." },
        new Message(){ Text = "TEST RECORD: Would you like a jelly baby?" },
        new Message(){ Text = "TEST RECORD: To the rational mind, " +
            "nothing is inexplicable; only unexplained." }
    };
}

SUT'nin veritabanı bağlamı içinde Program.cskaydedilir. Test uygulamasının builder.ConfigureServices geri çağırması, uygulamanın Program.cs kodu yürütüldükten sonra yürütülür. Testlerde farklı bir veritabanı kullanmak için uygulamanın veritabanı bağlamı içinde builder.ConfigureServicesdeğiştirilmelidir. Daha fazla bilgi için WebApplicationFactory'yi Özelleştirme bölümüne bakın.

Ek kaynaklar