Integrační testy v ASP.NET Core

Od : Cevarro Proced, Steve Smitha Jos van der Til

Integrační testy zajišťují, aby komponenty aplikace správně fungovaly na úrovni, která zahrnuje podpůrnou infrastrukturu aplikace, jako je databáze, systém souborů a síť. ASP.NET Core podporuje integrační testy pomocí rozhraní pro testování částí s testovacím webovým hostitelem a serverem testů v paměti.

Toto téma předpokládá základní znalosti o testech jednotek. Pokud koncepty testů neznáme, podívejte se na téma Testování částí v .NET Core a .NET Standard a související obsah.

Zobrazení nebo stažení ukázkového kódu (stažení)

Ukázková aplikace je aplikace Pages a Razor předpokládá základní znalosti o Razor stránkách. Pokud stránky nevidíte, projděte Razor si následující témata:

Poznámka

Pro testování rozhraní SPA doporučujeme nástroj, například Playwright pro .NET,který dokáže automatizovat prohlížeč.

Úvod do integračních testů

Integrační testy vyhodnocují komponenty aplikace na širší úrovni než testy jednotek. Testy jednotek se používají k testování izolovaných softwarových komponent, jako jsou metody jednotlivých tříd. Integrační testy potvrzují, že dvě nebo více komponent aplikace spolupracují, aby se vytvářel očekávaný výsledek, a to včetně všech komponent požadovaných k úplnému zpracování požadavku.

Tyto širší testy se používají k testování infrastruktury a celé architektury aplikace, často včetně následujících komponent:

  • databáze
  • Systém souborů
  • Síťová zařízení
  • Kanál požadavek-odpověď

Testy jednotek místo komponent infrastruktury používají fabricované komponenty, označované jako napodobenny nebo napodobené objekty.

Na rozdíl od testů jednotek integrační testy:

  • Použijte skutečné komponenty, které aplikace používá v produkčním prostředí.
  • Vyžadovat další zpracování kódu a dat.
  • Spuštění trvá delší dobu.

Proto omezte použití integračních testů na nejdůležitější scénáře infrastruktury. Pokud je možné chování testovat pomocí testu jednotek nebo integračního testu, zvolte test jednotek.

V diskuzích o integračních testech se testovaný projekt často nazývá System Under Test(Testovaný systém) nebo zkráceně "SUT". "SUT" se v tomto tématu používá k odkazování na testované ASP.NET Core aplikace.

Tip

Nepište integrační testy pro každou možnou permutaci dat a přístupu k souborům s databázemi a systémy souborů. Bez ohledu na to, kolik míst v aplikaci komunikuje s databázemi a systémy souborů, jsou integrační testy zaměřené na čtení, zápis, aktualizaci a odstranění obvykle schopné adekvátně testovat komponenty databáze a systému souborů. Testy jednotek použijte pro rutinní testy logiky metody, které komunikují s těmito komponentami. V testech jednotek je použití napodobeninou/napodobeninou infrastruktury výsledkem rychlejšího provádění testů.

ASP.NET Core integrační testy

Integrační testy v ASP.NET Core vyžadují následující:

  • Projekt testů slouží k obsahují a spouští testy. Projekt testů obsahuje odkaz na SUT.
  • Projekt testů vytvoří testovacího webového hostitele pro SUT a použije klienta testovacího serveru ke zpracování požadavků a odpovědí pomocí SUT.
  • Ke spuštění testů a hlášení výsledků testů se používá spouštěče testů.

Integrační testy se prochývají posloupností událostí, které zahrnují obvyklé kroky Uspořádat, Jednat a Vyhodnocení testu:

  1. Je nakonfigurovaný webový hostitel SUT.
  2. Vytvoří se klient testovacího serveru pro odesílání požadavků do aplikace.
  3. Spustí se testovací krok Uspořádat: Testovací aplikace připraví požadavek.
  4. Spustí se testovací krok Act: Klient odešle požadavek a obdrží odpověď.
  5. Spustí se testovací krok Assert: Skutečná odpověď se na základě očekávané odpovědi ověří jako průchod nebo selhání.
  6. Proces pokračuje, dokud nejsou provedeny všechny testy.
  7. Výsledky testu jsou hlášeny.

Testovací webový hostitel je obvykle nakonfigurovaný jinak než normální webový hostitel aplikace pro testovací běhy. Pro testy se například může použít jiná databáze nebo jiná nastavení aplikace.

Komponenty infrastruktury, jako je testovací webový hostitel a testovací server v paměti ( ), poskytuje nebo spravuje TestServer balíček Microsoft.AspNetCore.Mvc.Testing. Použití tohoto balíčku zjednodušuje vytváření a spouštění testů.

Balíček Microsoft.AspNetCore.Mvc.Testing zpracovává následující úlohy:

  • Zkopíruje soubor závislostí ( .deps ) z SUT do adresáře testovacího bin projektu.
  • Nastaví kořenovou hodnotu obsahu na kořen projektu SUT tak, aby se při spuštění testů zjistily statické soubory a stránky nebo zobrazení.
  • Poskytuje třídu WebApplicationFactory pro zjednodušení bootstrapování SUT pomocí TestServer .

Dokumentace k testům jednotek popisuje, jak nastavit projekt testů a spouštěče testů, spolu s podrobnými pokyny ke spouštění testů a doporučení k pojmení testů a testovacích tříd.

Poznámka

Při vytváření projektu testů pro aplikaci oddělte testy jednotek od integračních testů do různých projektů. To pomáhá zajistit, že součásti testování infrastruktury nebudou nechtěně zahrnuty do testů jednotek. Oddělení testů jednotek a integračních testů také umožňuje řídit, která sada testů se spustí.

V podstatě není žádný rozdíl mezi konfigurací pro testy aplikací Pages a Razor aplikacemi MVC. Jediným rozdílem je, jak jsou testy pojmenovány. V aplikaci Pages jsou testy koncových bodů stránky obvykle pojmenovány podle třídy modelu stránky (například k otestování integrace komponent Razor IndexPageTests pro stránku Index). V aplikaci MVC jsou testy obvykle uspořádány podle tříd kontrolerů a pojmenovány podle kontrolerů, které testují (například k otestování integrace komponent HomeControllerTests Home kontroleru).

Požadavky testovací aplikace

Projekt testů musí:

Tyto požadavky si můžete zobrazit v ukázkové aplikaci. Zkontrolujte tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj soubor . Ukázková aplikace používá testovací rozhraní xUnit a knihovnu analyzátoru AngleSharp, takže ukázková aplikace také odkazuje na:

V aplikacích, xunit.runner.visualstudio které používají verzi 2.4.2 nebo novější, musí projekt testování odkazovat na Microsoft.NET.Test.Sdk balíček.

Entity Framework Core se také používá v testech. Aplikace odkazuje na:

Prostředí SUT

Pokud prostředí SUT není nastavené, výchozí nastavení prostředí je Vývoj.

Základní testy s výchozí webovou aplikací WebApplicationFactory

WebApplicationFactory<TEntryPoint> se používá k vytvoření TestServer pro integrační testy. TEntryPoint je třída vstupních bodů SUT, obvykle Startup třída .

Testovací třídy implementují rozhraní pro správu tříd ( ), které označuje, že třída obsahuje testy a poskytuje instance sdílených IClassFixture objektů napříč testy ve třídě.

Následující testovací třída BasicTests používá metodu ke WebApplicationFactory spuštění SUT a k poskytnutí metody HttpClient testu Get_EndpointsReturnSuccessAndCorrectContentType . Metoda zkontroluje, jestli je stavový kód odpovědi úspěšný (stavové kódy v rozsahu 200–299) a jestli se hlavička nachází Content-Type text/html; charset=utf-8 na několika stránkách aplikace.

CreateClient vytvoří instanci , HttpClient která automaticky sleduje přesměrování a popisovače cookie s.

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

Ve výchozím nastavení se při povolení zásad souhlasu GDPR nezachovají jiné než cookie nezbytné požadavky. Pokud chcete zachovat jiné než nezbytné vlastnosti, například ty, které používá zprostředkovatel TempData, označte je cookie v testech jako nezbytné. Pokyny k označení jako cookie nezbytné najdete v části Základní cookie s.

Přizpůsobení WebApplicationFactory

Konfiguraci webového hostitele je možné vytvořit nezávisle na testovacích třídách tak, že z třídy dědíte a vytvoříte jednu nebo WebApplicationFactory více vlastních továren:

  1. Dědí z WebApplicationFactory a přepíše ConfigureWebHost . umožňuje IWebHostBuilder konfiguraci kolekce služby pomocí ConfigureServices :

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

    Dosytování databáze v ukázkové aplikaci se provádí InitializeDbForTests pomocí metody . Metoda je popsaná v ukázce integračních testů: Organizace testovací aplikace.

    Kontext databáze SUT je zaregistrovaný ve své Startup.ConfigureServices metodě. Zpětné volání testovací aplikace se spustí po spuštění kódu builder.ConfigureServices Startup.ConfigureServices aplikace. Pořadí provádění je změna typu obecná změna s vydáním verze ASP.NET Core 3.0. Pokud chcete pro testy použít jinou databázi než databázi aplikace, je nutné nahradit kontext databáze aplikace v builder.ConfigureServices .

    U SUT, které stále používají webového hostitele,se zpětné volání testovací aplikace provádí před builder.ConfigureServices kódem SUT. Startup.ConfigureServices Zpětné volání testovací aplikace builder.ConfigureTestServices je provedeno po.

    Ukázková aplikace vyhledá popisovač služby pro kontext databáze a použije popisovač k odebrání registrace služby. V dalším kroku továrna přidá novou ApplicationDbContext , která pro testy používá databázi v paměti.

    Chcete-li se připojit k jiné databázi než databáze v paměti, změňte UseInMemoryDatabase volání pro připojení kontextu k jiné databázi. použití testovací databáze SQL Server:

    services.AddDbContext<ApplicationDbContext>((options, context) => 
    {
        context.UseSqlServer(
            Configuration.GetConnectionString("TestingDbConnectionString"));
    });
    
  2. Použijte vlastní CustomWebApplicationFactory v testovacích třídách. Následující příklad používá objekt factory ve IndexPageTests třídě:

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

    Klient ukázkové aplikace je nakonfigurován tak, aby zabránil HttpClient následujícímu přesměrování. Jak je vysvětleno dále v části model ověřování , umožňuje testům kontrolovat výsledek první odpovědi aplikace. První odpověď je přesměrování v mnoha těchto testech s Location hlavičkou.

  3. Typický test používá HttpClient pomocné metody a ke zpracování žádosti a odpovědi:

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

Jakýkoli požadavek POST na SUT musí splňovat kontrolu proti padělání, kterou automaticky provádí systém ochrany dat proti padělání dataplikace. Aby bylo možné uspořádat požadavek POST testu, musí aplikace testů:

  1. Vytvořte požadavek na stránku.
  2. Analyzovat antipadělání cookie a žádat ověřovací token z odpovědi.
  3. Proveďte požadavek POST se žádostí o cookie ověření a vyžádat ověřovací token.

SendAsyncPomocné metody rozšíření ( Helpers/HttpClientExtensions.cs ) a GetDocumentAsync pomocná metoda ( Helpers/HtmlHelpers.cs ) v ukázkové aplikaci používají analyzátor AngleSharp ke zpracování kontroly proti padělání pomocí následujících metod:

  • GetDocumentAsync: Přijme HttpResponseMessage a vrátí IHtmlDocument . GetDocumentAsync používá objekt pro vytváření, který připraví virtuální odpověď na základě originálu HttpResponseMessage . Další informace najdete v dokumentaci k AngleSharp.
  • SendAsyncmetody rozšíření pro HttpClient vytvoření HttpRequestMessage a volání SendAsync(HttpRequestMessage ) k odeslání požadavků do SUT. Přetížení pro SendAsync přijměte formulář HTML ( IHtmlFormElement ) a následující:
    • Tlačítko Odeslat formuláře ( IHtmlElement )
    • Kolekce hodnot formulářů ( IEnumerable<KeyValuePair<string, string>> )
    • Odeslat tlačítko ( IHtmlElement ) a hodnoty formuláře ( IEnumerable<KeyValuePair<string, string>> )

Poznámka

AngleSharp je knihovna analýzy třetí strany, která se používá pro demonstrační účely v tomto tématu a v ukázkové aplikaci. AngleSharp se nepodporuje nebo nevyžaduje pro testování integrace ASP.NET Corech aplikací. Lze použít jiné analyzátory, jako je například HTML flexibility Pack (HAP). Další možností je napsat kód pro zpracování tokenu pro ověření žádosti systému System a jeho padělání cookie .

Přizpůsobení klienta pomocí WithWebHostBuilder

V případě, že v rámci testovací metody je vyžadována další konfigurace, WithWebHostBuilder vytvoří nový WebApplicationFactory s objektem IWebHostBuilder , který je dále přizpůsoben konfigurací.

Post_DeleteMessageHandler_ReturnsRedirectToRootTestovací metoda ukázkové aplikace demonstruje použití WithWebHostBuilder . Tento test provede v databázi odstranění záznamu aktivací formuláře v SUT.

Vzhledem k tomu, že jiný test ve IndexPageTests třídě provádí operaci, která odstraní všechny záznamy v databázi a může běžet před Post_DeleteMessageHandler_ReturnsRedirectToRoot metodou, databáze je v této testovací metodě znovu osazena, aby se zajistilo, že SUT k odstranění bude přítomen záznam. Výběr prvního tlačítka odstranit messages ve formuláři v SUT se simuluje v požadavku na SUT:

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

Možnosti klienta

V následující tabulce jsou uvedeny výchozí hodnoty, WebApplicationFactoryClientOptions které jsou k dispozici při vytváření HttpClient instancí.

Možnost Popis Výchozí
AllowAutoRedirect Získá nebo nastaví, jestli se HttpClient mají instance automaticky sledovat prostřednictvím odpovědí přesměrování. true
BaseAddress Získá nebo nastaví základní adresu HttpClient instancí. http://localhost
HandleCookies Získá nebo nastaví, jestli se HttpClient mají zpracovat instance cookie . true
MaxAutomaticRedirections Získá nebo nastaví maximální počet odpovědí přesměrování, které HttpClient by měly instance následovat. 7

Vytvořte WebApplicationFactoryClientOptions třídu a předejte ji do CreateClient metody (výchozí hodnoty jsou uvedeny v příkladu kódu):

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

Vložení makety služeb

Služby lze v rámci testu přepsat voláním ConfigureTestServices v Tvůrci hostitele. Pro vložení makety služeb musí mít SUT Startup třídu s Startup.ConfigureServices metodou.

Vzorový SUT obsahuje oborovou službu, která vrací citát. Nabídka je vložena do skrytého pole na stránce index, když je požadována stránka indexu.

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">

Při spuštění aplikace SUT se vygeneruje následující kód:

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

Chcete-li otestovat vkládání služby a uvozovek v rámci integračního testu, je do SUTu vložena přípravou služba. Napodobná služba nahradí aplikaci QuoteService službou poskytnutou testovací aplikací, která se nazývá TestQuoteService :

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 je volána a zaregistrována Oborová služba:

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

Označení vyprodukované během provádění testu odráží text citace, který je dodaný pomocí TestQuoteService , takže kontrolní výraz projde:

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

Modely ověřování

Testy ve AuthTests třídě zkontrolují zabezpečený koncový bod:

  • Přesměruje neověřeného uživatele na přihlašovací stránku aplikace.
  • Vrátí obsah pro ověřeného uživatele.

V SUT /SecurePage stránka používá AuthorizePage konvenci pro použití AuthorizeFilter na stránku. Další informace najdete v tématu Razor autorizační konvence pro stránky.

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

V Get_SecurePageRedirectsAnUnauthenticatedUser testu WebApplicationFactoryClientOptions je nastavené na zakázat přesměrování nastavením AllowAutoRedirect na false :

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

Když klientovi zakážete postup přesměrování, můžete provést následující kontroly:

  • Stavový kód vrácený SUT může být zkontrolován podle očekávaného HttpStatusCode.Redirect výsledku, nikoli z konečného stavového kódu po přesměrování na přihlašovací stránku, která by byla HttpStatusCode. ok.
  • LocationHodnota hlavičky v hlavičkách odpovědi je zaškrtnuta, aby se ověřilo, že začíná http://localhost/Identity/Account/Login , a ne konečná odpověď přihlašovací stránky, kde by tato hlavička nebyla k Location dispozici.

Testovací aplikace může AuthenticationHandler<TOptions> ConfigureTestServices napodobovat, aby mohla testovat aspekty ověřování a autorizace. Minimální scénář vrátí AuthenticateResult.Success :

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

TestAuthHandlerJe volána k ověření uživatele, pokud je schéma ověřování nastaveno na umístění, Test kde AddAuthentication je zaregistrováno pro ConfigureTestServices . Je důležité, Test aby schéma odpovídalo schématu, které vaše aplikace očekává. V opačném případě ověřování nebude fungovat.

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

Další informace o naleznete v WebApplicationFactoryClientOptions části Možnosti klienta .

Nastavení prostředí

Ve výchozím nastavení je hostitel a prostředí aplikace SUT nakonfigurovaná tak, aby používala vývojové prostředí. Přepsání prostředí SUT při použití IHostBuilder :

  • Nastavte ASPNETCORE_ENVIRONMENT proměnnou prostředí (například, Staging Production nebo jinou vlastní hodnotu, například Testing ).
  • Přepsání CreateHostBuilder v testovací aplikaci pro čtení proměnných prostředí s předponou ASPNETCORE .
protected override IHostBuilder CreateHostBuilder() =>
    base.CreateHostBuilder()
        .ConfigureHostConfiguration(
            config => config.AddEnvironmentVariables("ASPNETCORE"));

Pokud SUT používá webového hostitele ( IWebHostBuilder ), přepište CreateWebHostBuilder :

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

Jak testovací infrastruktura odvodí kořenovou cestu obsahu aplikace

WebApplicationFactoryKonstruktor odvodí kořenovou cestu obsahu aplikace hledáním na sestavení, které WebApplicationFactoryContentRootAttribute obsahuje testy integrace s klíčem, který se rovná TEntryPoint sestavení System.Reflection.Assembly.FullName . V případě, že atribut se správným klíčem nebyl nalezen, WebApplicationFactory vrátí se zpět k hledání souboru řešení (. sln) a připojí TEntryPoint název sestavení k adresáři řešení. Kořenový adresář aplikace (kořenová cesta obsahu) se používá ke zjišťování zobrazení a souborů obsahu.

Zakázat stínové kopírování

Stínové kopírování způsobí, že se testy spustí v jiném adresáři než výstupní adresář. Pokud vaše testy spoléhají na načítání souborů vzhledem k Assembly.Location problémům a narazíte na problémy, může být nutné zakázat stínové kopírování.

Chcete-li zakázat stínové kopírování při použití xUnit, vytvořte xunit.runner.json soubor v adresáři testovacího projektu se správným nastavením konfigurace:

{
  "shadowCopy": false
}

Vyřazení objektů

Po provedení testů pro IClassFixture implementaci TestServer a HttpClient jsou uvolněny, když xUnit odstraní WebApplicationFactory . Pokud objekty, které vytvořil vývojář, vyžadují likvidaci, jejich uvolnění v IClassFixture implementaci. Další informace naleznete v tématu implementace metody Dispose.

Ukázka integračních testů

Ukázková aplikace se skládá ze dvou aplikací:

Aplikace Adresář projektu Popis
Aplikace zprávy (SUT) src/RazorPagesProject Povoluje uživateli přidávat, odstraňovat a analyzovat zprávy a analyzovat je.
Testovací aplikace tests/RazorPagesProject.Tests Používá se k testování integrace SUT.

Testy lze spustit pomocí integrovaných testovacích funkcí integrovaného vývojového prostředí, jako je Visual Studio. Pokud používáte Visual Studio Code nebo příkazový řádek, spusťte na příkazovém řádku v adresáři následující tests/RazorPagesProject.Tests příkaz:

dotnet test

Organizace aplikace zpráv (SUT)

SUT je systém Razor zpráv Pages s následujícími charakteristikami:

  • Stránka Index aplikace ( a ) poskytuje metody uživatelského rozhraní a modelu stránky pro řízení přidávání, odstraňování a analýzy zpráv Pages/Index.cshtml Pages/Index.cshtml.cs (průměrná slova na zprávu).
  • Zpráva je popsána Message třídou ( Data/Message.cs ) se dvěma vlastnostmi: Id (klíč) a Text (zpráva). Vlastnost Text je povinná a omezená na 200 znaků.
  • Zprávy se ukládají pomocí Entity Framework databáze v paměti†.
  • Aplikace obsahuje vrstvu přístupu k datům (DAL) ve třídě kontextu databáze AppDbContext ( Data/AppDbContext.cs ).
  • Pokud je databáze při spuštění aplikace prázdná, inicializuje se úložiště zpráv se třemi zprávami.
  • Aplikace obsahuje objekt /SecurePage , ke kterým má přístup pouze ověřený uživatel.

†tématu EF, Test with InMemory, vysvětluje, jak používat databázi v paměti pro testy pomocí MSTestu. Toto téma používá testovací rozhraní xUnit. Koncepty testů a implementace testů v různých testovacích architekturách jsou podobné, ale nejsou identické.

I když aplikace vzor úložiště nevyu používá a není efektivním příkladem vzoru UoW (Unit of Work),Pages podporuje tyto vzory Razor vývoje. Další informace najdete v tématu Návrh vrstvy trvalosti infrastruktury a Logika testovacího kontroleru (ukázka implementuje vzor úložiště).

Otestování organizace aplikace

Testovací aplikace je konzolová aplikace v tests/RazorPagesProject.Tests adresáři .

Testování adresáře aplikace Description
AuthTests Obsahuje testovací metody pro:
  • Přístup k zabezpečené stránce neověřeným uživatelem
  • Ověřený uživatel přistupuje k zabezpečené stránce pomocí napodobné stránky AuthenticationHandler<TOptions> .
  • Získání GitHub profilu uživatele a kontrola přihlášení uživatele v profilu.
BasicTests Obsahuje testovací metodu pro směrování a typ obsahu.
IntegrationTests Obsahuje integrační testy pro indexovou stránku pomocí vlastní WebApplicationFactory třídy .
Helpers/Utilities
  • Utilities.cs obsahuje InitializeDbForTests metodu, která slouží k dosaování databáze testovacími daty.
  • HtmlHelpers.cs poskytuje metodu pro vrácení AngleSharp IHtmlDocument pro použití testovacími metodami.
  • HttpClientExtensions.cs poskytuje přetížení pro SendAsync k odesílání požadavků na SUT.

Testovací rozhraní je xUnit. Integrační testy se provádějí pomocí Microsoft.AspNetCore.TestHost , který zahrnuje TestServer . Vzhledem k tomu, že se balíček používá ke konfiguraci testovacího hostitele a testovacího serveru, nevyžadují balíčky a přímé odkazy na balíčky v souboru projektu testovací aplikace ani v konfiguraci vývojáře v Microsoft.AspNetCore.Mvc.Testing TestHost testovací TestServer aplikaci.

Integrační testy obvykle před provedením testu vyžadují malou datovou sadu v databázi. Například testovací odstranění volá odstranění záznamu databáze, takže databáze musí mít alespoň jeden záznam, aby byla žádost o odstranění úspěšná.

Ukázková aplikace do databáze přisudí tři Utilities.cs zprávy, které mohou testy použít při jejich spuštění:

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

Kontext databáze SUT je zaregistrovaný ve své Startup.ConfigureServices metodě. Zpětné volání testovací aplikace se spustí po spuštění kódu builder.ConfigureServices Startup.ConfigureServices aplikace. Pokud chcete pro testy použít jinou databázi, je nutné nahradit kontext databáze aplikace v builder.ConfigureServices . Další informace najdete v části Přizpůsobení WebApplicationFactory.

U SUT, které stále používají webového hostitele,se zpětné volání testovací aplikace provádí před builder.ConfigureServices kódem SUT. Startup.ConfigureServices Zpětné volání testovací builder.ConfigureTestServices aplikace se spustí po.

Další zdroje informací