Integrační testy v ASP.NET Core

Jos van der Til, Martin Costello a Javier Calvarro Nelson.

Integrační testy zajišťují správné fungování komponent aplikace na úrovni, která zahrnuje podpůrnou infrastrukturu aplikace, jako je databáze, systém souborů a síť. ASP.NET Core podporuje integrační testy s využitím architektury testování jednotek s testovacím webovým hostitelem a testovacím serverem v paměti.

Tento článek předpokládá základní znalosti testů jednotek. Pokud neznáte koncepty testů, podívejte se na článek o testování částí v .NET Core a .NET Standard a jeho propojeném obsahu.

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

Ukázková aplikace je Razor aplikace Pages a předpokládá základní znalost Razor stránek. Pokud stránky neznáte Razor , přečtěte si následující články:

Pro testování spA doporučujeme nástroj, jako je Playwright pro .NET, který můž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 například jednotlivé metody tříd. Testy integrace ověřují, že dvě nebo více součástí aplikace spolupracují a vytvářejí očekávaný výsledek, a to včetně všech komponent potřebných k úplnému zpracování požadavku.

Tyto širší testy se používají k otestová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 odpovědi požadavku

Testy jednotek používají prostředky infrastruktury, označované jako falešné nebo napodobené objekty, místo komponent infrastruktury.

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

  • Použijte skutečné komponenty, které aplikace používá v produkčním prostředí.
  • Vyžaduje více kódu a zpracování dat.
  • Spusťte delší dobu.

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

V diskusích o integračních testech se testovaný projekt často nazývá systém pod testem nebo "SUT". "SUT" se používá v tomto článku k odkazování na aplikaci ASP.NET Core, která se testuje.

Nezapisujte integrační testy pro každou 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ů, je obvykle schopná dostatečně testovat databáze a součásti systému souborů zaměřenou na čtení, zápis, aktualizaci a odstranění. Pro rutinní testy logiky metody, které s těmito komponentami pracují, použijte testy jednotek. Při testování jednotek může použití falešných nebo napodobených infrastruktur vést k rychlejšímu provádění testů.

testy integrace ASP.NET Core

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

  • Projekt testů se používá k zahrnutí a provádění testů. Testovací projekt má odkaz na SUT.
  • Testovací projekt 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.
  • Spouštěč testů se používá k provádění testů a hlášení výsledků testu.

Integrační testy se řídí posloupností událostí, které zahrnují obvyklé testovací kroky Uspořádat, Jednat a Assert :

  1. Webový hostitel SUT je nakonfigurovaný.
  2. Vytvoří se testovací serverový klient 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 actu : Klient odešle požadavek a obdrží odpověď.
  5. Provede se testovací krok Assert: Skutečnáodpověď se ověří jako úspěšné nebo neúspěšné na základě očekávané odpovědi.
  6. Proces pokračuje, dokud se všechny testy nespustí.
  7. Výsledky testu jsou hlášeny.

Obvykle je testovací webový hostitel 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 (TestServer), jsou poskytovány nebo spravovány balíčkem 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řen obsahu na kořen projektu SUT tak, aby se při spuštění testů zjistily statické soubory a stránky/zobrazení.
  • Poskytuje WebApplicationFactory třídy pro zjednodušení bootstrapping SUT s TestServer.

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

Oddělte testy jednotek od integračních testů do různých projektů. Oddělení testů:

  • Pomáhá zajistit, aby součásti testování infrastruktury nejsou omylem zahrnuty do testů jednotek.
  • Umožňuje kontrolu nad tím, jakou sadu testů se spouští.

Konfigurace pro testy Razor aplikací Pages a aplikací MVC není prakticky nijak rozdílná. Jediný rozdíl spočívá v tom, jak jsou testy pojmenovány. Razor V aplikaci Pages jsou testy koncových bodů stránky obvykle pojmenovány za třídou modelu stránky (například IndexPageTests pro testování integrace součástí pro indexovou stránku). V aplikaci MVC jsou testy obvykle uspořádány podle tříd kontroleru a pojmenovány podle kontrolerů, které testují (například HomeControllerTests pro testování integrace součástí pro Home kontroler).

Požadavky na testovací aplikaci

Testovací projekt musí:

Tyto požadavky se dají zobrazit v ukázkové aplikaci. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Zkontrolujte soubor. Ukázková aplikace používá testovací architekturu xUnit a knihovnu analyzátoru AngleSharp , takže ukázková aplikace také odkazuje:

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

Entity Framework Core se také používá v testech. Prohlédněte si soubor projektu na GitHubu.

Prostředí SUT

Pokud prostředí SUT není nastavené, prostředí se ve výchozím nastavení nastaví na Vývoj.

Základní testy s výchozím webapplicationFactory

Implicitně definovanou Program třídu zpřístupňte testovacímu projektu jedním z následujících způsobů:

  • Zveřejnění interních typů z webové aplikace do testovacího projektu To lze provést v souboru projektu SUT (.csproj):

    <ItemGroup>
         <InternalsVisibleTo Include="MyTestProject" />
    </ItemGroup>
    
  • Program Nastavení třídy jako veřejné pomocí částečné deklarace třídy:

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

    Ukázková aplikace používá Program přístup částečné třídy.

WebApplicationFactory<TEntryPoint> slouží k vytvoření TestServer integračních testů. TEntryPoint je vstupní bod třídy SUT, obvykle Program.cs.

Třídy testů implementují rozhraní příslušenství třídy (IClassFixture) k označení třídy obsahuje testy a poskytují sdílené instance objektů napříč testy ve třídě.

Následující testovací třída , BasicTestspoužívá WebApplicationFactory bootstrap SUT a poskytnout HttpClient testovací metodu, Get_EndpointsReturnSuccessAndCorrectContentType. Metoda ověří, že stavový kód odpovědi je úspěšný (200–299) a hlavička Content-Type je text/html; charset=utf-8 pro několik stránek aplikace.

CreateClient() vytvoří instanci HttpClient , která automaticky sleduje přesměrování a zpracovává cookies.

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

Ve výchozím nastavení se v případě povolení zásad souhlasu obecného nařízení o ochraně osobních údajů nezachovávají základní cookiepožadavky. Pokud chcete zachovat jiné než podstatné cookiehodnoty, například ty, které používá poskytovatel TempData, označte je v testech jako nezbytné. Pokyny k označení cookie jako nezbytné naleznete v tématu Základní cookies.

AngleSharp vs Application Parts . pro antiforgery kontroly

Tento článek používá analyzátor AngleSharp ke zpracování antiforgery kontroly načtením stránek a parsováním HTML. Pokud chcete otestovat koncové body zobrazení kontroleru a Razor stránek na nižší úrovni, aniž byste se museli zabývat tím, jak se vykreslují v prohlížeči, zvažte použití Application Parts. Přístup části aplikace vloží kontroler nebo Razor stránku do aplikace, které lze použít k provádění JSpožadavků ON, aby získaly požadované hodnoty. Další informace najdete v blogu o testování integrace ASP.NET základních prostředků chráněných antiforgery pomocí částí aplikace a přidruženého úložiště GitHub od Martin Costello.

Přizpůsobení služby WebApplicationFactory

Konfiguraci webového hostitele lze vytvořit nezávisle na testovacích třídách tak, že zdědíte WebApplicationFactory<TEntryPoint> vytvoření jedné nebo více vlastních továren:

  1. Dědit z WebApplicationFactory a přepsat ConfigureWebHost. Umožňuje IWebHostBuilder konfiguraci kolekce služeb pomocí 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");
        }
    }
    

    Počáteční databázová v ukázkové aplikaci se provádí metodou InitializeDbForTests . Metoda je popsaná v ukázce integračních testů: Část organizace testovací aplikace.

    Kontext databáze SUT je registrován v Program.cs. Zpětné volání testovací aplikace builder.ConfigureServices se spustí po spuštění kódu aplikace Program.cs . Chcete-li použít jinou databázi pro testy, než je databáze aplikace, musí být kontext databáze aplikace nahrazen v builder.ConfigureServices.

    Ukázková aplikace najde popisovač služby pro kontext databáze a pomocí popisovače odebere registraci služby. Továrna pak přidá novou ApplicationDbContext databázi v paměti pro testy..

    Pokud se chcete připojit k jiné databázi, změňte soubor DbConnection. Použití testovací databáze SQL Serveru:

  1. Použijte vlastní CustomWebApplicationFactory v testovacích třídách. Následující příklad používá továrnu IndexPageTests ve třídě:

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

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

  2. Typický test používá HttpClient metody a pomocné metody ke zpracování požadavku 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);
    }
    

Všechny požadavky POST na SUT musí splňovat antiforgery kontrolu, která je automaticky provedena systémem ochrany dat aplikace. Aby bylo možné zajistit požadavek POST testu, musí testovací aplikace:

  1. Vytvořte žádost o stránku.
  2. Parsujte antiforgery cookie a požadavek na ověřovací token z odpovědi.
  3. Vytvořte požadavek POST s použitím antiforgery cookie a ověřovacího tokenu požadavku.

Pomocné SendAsync rozšiřující metody (Helpers/HttpClientExtensions.cs) a GetDocumentAsync pomocná metoda (Helpers/HtmlHelpers.cs) v ukázkové aplikaci používají analyzátor AngleSharp ke zpracování kontroly antiforgery následujícími metodami:

  • GetDocumentAsync: Přijme HttpResponseMessage a vrátí hodnotu IHtmlDocument. GetDocumentAsync používá továrnu, která připraví virtuální odpověď na základě původní HttpResponseMessage. Další informace najdete v dokumentaci k sadě AngleSharp.
  • SendAsync metody rozšíření pro HttpClient vytvoření žádosti HttpRequestMessage a volání SendAsync(HttpRequestMessage) k odeslání žádostí do SUT. Přetížení pro SendAsync přijetí html formuláře (IHtmlFormElement) a následující:
    • Tlačítko Odeslat formulář (IHtmlElement)
    • Kolekce hodnot formulářů (IEnumerable<KeyValuePair<string, string>>)
    • Tlačítko Odeslat (IHtmlElement) a hodnoty formuláře (IEnumerable<KeyValuePair<string, string>>)

AngleSharp je knihovna analýzy třetí strany používaná pro demonstrační účely v tomto článku a ukázkové aplikaci. AngleSharp se nepodporuje ani nevyžaduje pro testování integrace aplikací ASP.NET Core. Další analyzátory je možné použít, například html agilní balíček (HAP). Dalším přístupem je napsat kód pro zpracování ověřovacího tokenu žádosti systému antiforgery a antiforgery cookie přímo. Další informace najdete v části AngleSharp vs Application Parts . antiforgery kontroly v tomto článku.

Zprostředkovatel databáze EF-Core v paměti lze použít pro omezené a základní testování, ale pro testování v paměti se doporučuje poskytovatel SQLite.

Viz Rozšíření po spuštění filtry , které ukazují, jak nakonfigurovat middleware pomocí IStartupFilter, což je užitečné, když test vyžaduje vlastní službu nebo middleware.

Přizpůsobení klienta pomocí withWebHostBuilder

Pokud je v rámci testovací metody vyžadována další konfigurace, WithWebHostBuilder vytvoří novou WebApplicationFactory konfiguraci IWebHostBuilder , která je dále přizpůsobena konfigurací.

Ukázkové volání WithWebHostBuilder kódu, která nahradí nakonfigurované služby testovacími zástupnými procedurami. Další informace a příklady použití najdete v části Vložení napodobených služeb v tomto článku.

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

Protož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 bude znovu provedena v této testovací metodě, aby se zajistilo, že záznam existuje pro SUT k odstranění. Výběr prvního tlačítka messages pro odstranění formuláře v SUT se simuluje v požadavku na SUT:

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

Možnosti klienta

Podívejte se na WebApplicationFactoryClientOptions stránku s výchozími a dostupnými možnostmi při vytváření HttpClient instancí.

WebApplicationFactoryClientOptions Vytvořte třídu a předejte ji metoděCreateClient():

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

POZNÁMKA: Pokud se chcete vyhnout upozorněním přesměrování HTTPS v protokolech při použití middlewaru přesměrování HTTPS, nastavte BaseAddress = new Uri("https://localhost")

Vložení napodobených služeb

Služby je možné přepsat v testu voláním ConfigureTestServices tvůrce hostitelů. K určení rozsahu přepsané služby na samotný test se WithWebHostBuilder metoda používá k načtení tvůrce hostitelů. To lze vidět v následujících testech:

Ukázková SUT obsahuje vymezenou službu, která vrací nabídku. Citace je vložena do skrytého pole na stránce Index, když se požaduje indexová stránka.

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

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

K otestování injektáže služby a nabídky v integračním testu se do testu vloží napodobená služba. Služba napodobení nahradí aplikaci QuoteService službou poskytovanou 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(
            "Something's interfering with time, Mr. Scarman, " +
            "and time is my business.");
    }
}

ConfigureTestServices je volána a služba s vymezeným oborem je zaregistrovaná:

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

Kód vytvořený během provádění testu odráží text uvozovek zadaný tímto výrazem TestQuoteService, a proto kontrolní výraz projde:

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

Napodobení ověřování

Testy ve AuthTests třídě kontrolují, že zabezpečený koncový bod:

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

V SUT stránka /SecurePage používá AuthorizePage konvenci k použití AuthorizeFilter stránky. Další informace najdete v tématu Razor Konvence autorizace stránek.

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

Get_SecurePageRedirectsAnUnauthenticatedUser V testu WebApplicationFactoryClientOptions je nastavena možnost zakázat přesměrování nastavením AllowAutoRedirect nafalse:

[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ž zakážete klientovi sledovat přesměrování, můžete provést následující kontroly:

  • Stavový kód vrácený SUT lze zkontrolovat podle očekávaného HttpStatusCode.Redirect výsledku, nikoli konečný stavový kód po přesměrování na přihlašovací stránku, což by bylo HttpStatusCode.OK.
  • Hodnota záhlaví v hlavičce odpovědi je zaškrtnutá, Location aby se ověřilo, že začíná http://localhost/Identity/Account/Login, ne poslední přihlašovací stránka odpovědi, kde Location by záhlaví nebylo k dispozici.

Testovací aplikace může AuthenticationHandler<TOptions>ConfigureTestServices 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)
        : 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);
    }
}

Volá TestAuthHandler se k ověření uživatele, pokud je schéma ověřování nastaveno na TestScheme místo, kde AddAuthentication je registrován ConfigureTestServices. Je důležité, TestScheme aby schéma odpovídalo schématu, které vaše aplikace očekává. Jinak ověřování nebude fungovat.

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

Další informace najdete WebApplicationFactoryClientOptionsv části Možnosti klienta.

Základní testy pro middleware ověřování

Základní testy middlewaru ověřování najdete v tomto úložišti GitHubu. Obsahuje testovací server specifický pro testovací scénář.

Nastavení prostředí

Nastavte prostředí ve vlastní objektu pro vytváření aplikací:

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

Způsob, jakým testovací infrastruktura odvodí kořenovou cestu obsahu aplikace

Konstruktor WebApplicationFactory odvodí kořenovou cestu obsahu aplikace tak, že vyhledá WebApplicationFactoryContentRootAttribute v sestavení obsahující integrační testy s klíčem, který se rovná TEntryPoint sestavení System.Reflection.Assembly.FullName. Pokud se atribut se správným klíčem nenajde, WebApplicationFactory vrátí se zpět do hledání souboru řešení (.sln) a připojí TEntryPoint název sestavení do adresáře řešení. Kořenový adresář aplikace (kořenová cesta k obsahu) slouží ke zjišťování zobrazení a souborů obsahu.

Zakázání stínové kopie

Stínová kopie způsobí, že testy se spustí v jiném adresáři než ve výstupním adresáři. Pokud vaše testy spoléhají na načítání souborů vzhledem k Assembly.Location problémům a dochází k problémům, možná budete muset stínové kopírování zakázat.

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

{
  "shadowCopy": false
}

Likvidace objektů

Po provedení testů IClassFixture implementace a HttpClient jsou uvolněny při xUnit disposes of the WebApplicationFactory. TestServer Pokud instance objektů vytvořených vývojářem vyžaduje odstranění, odstraňte je v implementaci IClassFixture . 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áv (SUT) src/RazorPagesProject Umožňuje uživateli přidávat, odstraňovat, odstraňovat všechny a analyzovat zprávy.
Testovací aplikace tests/RazorPagesProject.Tests Používá se k integraci testování SUT.

Testy je možné spouštět pomocí integrovaných testovacích funkcí integrovaného vývojového prostředí (IDE), například sady Visual Studio. Pokud používáte Visual Studio Code nebo příkazový řádek, spusťte v adresáři tests/RazorPagesProject.Tests následující příkaz:

dotnet test

Organizace aplikace zpráv (SUT)

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

  • Indexová stránka aplikace (Pages/Index.cshtml a Pages/Index.cshtml.cs) poskytuje uživatelské rozhraní a metody modelu stránky pro řízení sčítání, odstranění a analýzy zpráv (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í databáze v paměti entity Framework†.
  • Aplikace obsahuje vrstvu přístupu k datům (DAL) ve své třídě AppDbContext kontextu databáze (Data/AppDbContext.cs).
  • Pokud je databáze při spuštění aplikace prázdná, úložiště zpráv se inicializuje se třemi zprávami.
  • Aplikace zahrnuje /SecurePage přístup pouze ověřeným uživatelem.

†Pokudování v článku EF, Test s InMemory, vysvětluje, jak používat databázi v paměti pro testy s MSTest. Toto téma používá testovací architekturu xUnit . Koncepty testů a implementace testů v různých testovacích architekturách jsou podobné, ale ne identické.

I když aplikace nepoužívá vzor úložiště a není efektivním příkladem vzoruRazor Práce (UoW), stránky podporují tyto vzory vývoje. Další informace najdete v tématu Návrh vrstvy trvalosti infrastruktury a logiky kontroleru testů (ukázka implementuje vzor úložiště).

Testovací organizace aplikací

Testovací aplikace je konzolová aplikace uvnitř tests/RazorPagesProject.Tests adresáře.

Testovací adresář aplikace Popis
AuthTests Obsahuje testovací metody pro:
  • Přístup k zabezpečené stránce neověřeným uživatelem
  • Přístup k zabezpečené stránce ověřeným uživatelem pomocí napodobení AuthenticationHandler<TOptions>.
  • Získání profilu uživatele GitHubu a kontrola přihlášení uživatele profilu
BasicTests Obsahuje testovací metodu pro směrování a typ obsahu.
IntegrationTests Obsahuje integrační testy stránky indexu pomocí vlastní WebApplicationFactory třídy.
Helpers/Utilities
  • Utilities.cs obsahuje metodu použitou InitializeDbForTests k vytvoření databáze s 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 odesílání žádostí do SUT.

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

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

Ukázková aplikace vytvoří databázi se třemi zprávami v Utilities.cs tom, že testy můžou při spuštění použít:

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 registrován v Program.cs. Zpětné volání testovací aplikace builder.ConfigureServices se spustí po spuštění kódu aplikace Program.cs . Chcete-li pro testy použít jinou databázi, musí být kontext databáze aplikace nahrazen v builder.ConfigureServices. Další informace najdete v části Přizpůsobit webApplicationFactory .

Další materiály

V tomto tématu se předpokládá základní znalost testů jednotek. Pokud neznáte koncepty testů, přečtěte si téma Testování částí v tématech .NET Core a .NET Standard a jeho propojený obsah.

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

Ukázková aplikace je Razor aplikace Pages a předpokládá základní znalost Razor stránek. Pokud stránky neznáte Razor , projděte si následující témata:

Poznámka:

Pro testování spA doporučujeme nástroj, jako je Playwright pro .NET, který můž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 například jednotlivé metody tříd. Testy integrace ověřují, že dvě nebo více součástí aplikace spolupracují a vytvářejí očekávaný výsledek, a to včetně všech komponent potřebných k úplnému zpracování požadavku.

Tyto širší testy se používají k otestová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 odpovědi požadavku

Testy jednotek používají prostředky infrastruktury, označované jako falešné nebo napodobené objekty, místo komponent infrastruktury.

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

  • Použijte skutečné komponenty, které aplikace používá v produkčním prostředí.
  • Vyžaduje více kódu a zpracování dat.
  • Spusťte delší dobu.

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

V diskusích o integračních testech se testovaný projekt často nazývá systém pod testem nebo "SUT". "SUT" se používá v tomto článku k odkazování na aplikaci ASP.NET Core, která se testuje.

Nezapisujte integrační testy pro každou 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ů, je obvykle schopná dostatečně testovat databáze a součásti systému souborů zaměřenou na čtení, zápis, aktualizaci a odstranění. Pro rutinní testy logiky metody, které s těmito komponentami pracují, použijte testy jednotek. Při testování jednotek může použití falešných nebo napodobených infrastruktur vést k rychlejšímu provádění testů.

testy integrace ASP.NET Core

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

  • Projekt testů se používá k zahrnutí a provádění testů. Testovací projekt má odkaz na SUT.
  • Testovací projekt 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.
  • Spouštěč testů se používá k provádění testů a hlášení výsledků testu.

Integrační testy se řídí posloupností událostí, které zahrnují obvyklé testovací kroky Uspořádat, Jednat a Assert :

  1. Webový hostitel SUT je nakonfigurovaný.
  2. Vytvoří se testovací serverový klient 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 actu : Klient odešle požadavek a obdrží odpověď.
  5. Provede se testovací krok Assert: Skutečnáodpověď se ověří jako úspěšné nebo neúspěšné na základě očekávané odpovědi.
  6. Proces pokračuje, dokud se všechny testy nespustí.
  7. Výsledky testu jsou hlášeny.

Obvykle je testovací webový hostitel 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 (TestServer), jsou poskytovány nebo spravovány balíčkem 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řen obsahu na kořen projektu SUT tak, aby se při spuštění testů zjistily statické soubory a stránky/zobrazení.
  • Poskytuje WebApplicationFactory třídy pro zjednodušení bootstrapping SUT s TestServer.

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

Oddělte testy jednotek od integračních testů do různých projektů. Oddělení testů:

  • Pomáhá zajistit, aby součásti testování infrastruktury nejsou omylem zahrnuty do testů jednotek.
  • Umožňuje kontrolu nad tím, jakou sadu testů se spouští.

Konfigurace pro testy Razor aplikací Pages a aplikací MVC není prakticky nijak rozdílná. Jediný rozdíl spočívá v tom, jak jsou testy pojmenovány. Razor V aplikaci Pages jsou testy koncových bodů stránky obvykle pojmenovány za třídou modelu stránky (například IndexPageTests pro testování integrace součástí pro indexovou stránku). V aplikaci MVC jsou testy obvykle uspořádány podle tříd kontroleru a pojmenovány podle kontrolerů, které testují (například HomeControllerTests pro testování integrace součástí pro Home kontroler).

Požadavky na testovací aplikaci

Testovací projekt musí:

Tyto požadavky se dají zobrazit v ukázkové aplikaci. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Zkontrolujte soubor. Ukázková aplikace používá testovací architekturu xUnit a knihovnu analyzátoru AngleSharp , takže ukázková aplikace také odkazuje:

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

Entity Framework Core se také používá v testech. Odkazy na aplikaci:

Prostředí SUT

Pokud prostředí SUT není nastavené, prostředí se ve výchozím nastavení nastaví na Vývoj.

Základní testy s výchozím webapplicationFactory

WebApplicationFactory<TEntryPoint> slouží k vytvoření TestServer integračních testů. TEntryPoint je třída vstupního bodu SUT, obvykle Startup třídy.

Třídy testů implementují rozhraní příslušenství třídy (IClassFixture) k označení třídy obsahuje testy a poskytují sdílené instance objektů napříč testy ve třídě.

Následující testovací třída , BasicTestspoužívá WebApplicationFactory bootstrap SUT a poskytnout HttpClient testovací metodu, Get_EndpointsReturnSuccessAndCorrectContentType. Metoda zkontroluje, jestli je stavový kód odpovědi úspěšný (stavové kódy v rozsahu 200–299) a Content-Type záhlaví obsahuje text/html; charset=utf-8 několik stránek aplikace.

CreateClient() vytvoří instanci HttpClient , která automaticky sleduje přesměrování a zpracovává cookies.

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 v případě povolení zásad souhlasu GDPR nezachovají základní cookiepožadavky. Pokud chcete zachovat jiné než podstatné cookiehodnoty, například ty, které používá poskytovatel TempData, označte je v testech jako nezbytné. Pokyny k označení cookie jako nezbytné naleznete v tématu Základní cookies.

Přizpůsobení služby WebApplicationFactory

Konfiguraci webového hostitele lze vytvořit nezávisle na testovacích třídách tak, že zdědíte WebApplicationFactory vytvoření jedné nebo více vlastních továren:

  1. Dědit z WebApplicationFactory a přepsat ConfigureWebHost. Umožňuje IWebHostBuilder konfiguraci kolekce služeb 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);
                    }
                }
            });
        }
    }
    

    Počáteční databázová v ukázkové aplikaci se provádí metodou InitializeDbForTests . Metoda je popsaná v ukázce integračních testů: Část organizace testovací aplikace.

    Kontext databáze SUT je registrován v jeho Startup.ConfigureServices metodě. Zpětné volání testovací aplikace builder.ConfigureServices se spustí po spuštění kódu aplikace Startup.ConfigureServices . Pořadí provádění je zásadní změnou obecného hostitele s vydáním ASP.NET Core 3.0. Chcete-li použít jinou databázi pro testy, než je databáze aplikace, musí být kontext databáze aplikace nahrazen v builder.ConfigureServices.

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

    Ukázková aplikace najde popisovač služby pro kontext databáze a pomocí popisovače odebere registraci služby. Dále továrna přidá novou ApplicationDbContext databázi v paměti pro testy.

    Pokud se chcete 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 Serveru:

    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á továrnu IndexPageTests ve 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 nakonfigurovaný tak, aby zabránil HttpClient následujícím přesměrováním. Jak je vysvětleno později v části Napodobení ověřování , umožňuje testům zkontrolovat výsledek první odpovědi aplikace. První odpověď je přesměrování v mnoha těchto testech s hlavičkou Location .

  3. Typický test používá HttpClient metody a pomocné metody ke zpracování požadavku 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);
    }
    

Všechny požadavky POST na SUT musí splňovat antiforgery kontrolu, která je automaticky provedena systémem ochrany dat aplikace. Aby bylo možné zajistit požadavek POST testu, musí testovací aplikace:

  1. Vytvořte žádost o stránku.
  2. Parsujte antiforgery cookie a požadavek na ověřovací token z odpovědi.
  3. Vytvořte požadavek POST s použitím antiforgery cookie a ověřovacího tokenu požadavku.

Pomocné SendAsync rozšiřující metody (Helpers/HttpClientExtensions.cs) a GetDocumentAsync pomocná metoda (Helpers/HtmlHelpers.cs) v ukázkové aplikaci používají analyzátor AngleSharp ke zpracování kontroly antiforgery následujícími metodami:

  • GetDocumentAsync: Přijme HttpResponseMessage a vrátí hodnotu IHtmlDocument. GetDocumentAsync používá továrnu, která připraví virtuální odpověď na základě původní HttpResponseMessage. Další informace najdete v dokumentaci k sadě AngleSharp.
  • SendAsync metody rozšíření pro HttpClient vytvoření žádosti HttpRequestMessage a volání SendAsync(HttpRequestMessage) k odeslání žádostí do SUT. Přetížení pro SendAsync přijetí html formuláře (IHtmlFormElement) a následující:
    • Tlačítko Odeslat formulář (IHtmlElement)
    • Kolekce hodnot formulářů (IEnumerable<KeyValuePair<string, string>>)
    • Tlačítko Odeslat (IHtmlElement) a hodnoty formuláře (IEnumerable<KeyValuePair<string, string>>)

Poznámka:

AngleSharp je knihovna analýzy třetí strany používaná pro demonstrační účely v tomto tématu a ukázkovou aplikaci. AngleSharp se nepodporuje ani nevyžaduje pro testování integrace aplikací ASP.NET Core. Další analyzátory je možné použít, například html agilní balíček (HAP). Dalším přístupem je napsat kód pro zpracování ověřovacího tokenu žádosti systému antiforgery a antiforgery cookie přímo.

Poznámka:

Zprostředkovatel databáze EF-Core v paměti lze použít pro omezené a základní testování, ale pro testování v paměti se doporučuje poskytovatel SQLite.

Přizpůsobení klienta pomocí withWebHostBuilder

Pokud je v rámci testovací metody vyžadována další konfigurace, WithWebHostBuilder vytvoří novou WebApplicationFactory konfiguraci IWebHostBuilder , která je dále přizpůsobena konfigurací.

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

Protož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 bude znovu provedena v této testovací metodě, aby se zajistilo, že záznam existuje pro SUT k odstranění. Výběr prvního tlačítka messages pro odstranění formuláře 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

Následující tabulka ukazuje výchozí WebApplicationFactoryClientOptions dostupnou možnost při vytváření HttpClient instancí.

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

WebApplicationFactoryClientOptions Vytvořte třídu a předejte ji CreateClient() metodě (výchozí hodnoty se zobrazují 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í napodobených služeb

Služby je možné přepsat v testu voláním ConfigureTestServices tvůrce hostitelů. K vložení napodobených služeb musí SUT mít Startup třídu s metodou Startup.ConfigureServices .

Ukázková SUT obsahuje vymezenou službu, která vrací nabídku. Citace je vložena do skrytého pole na stránce Index, když se požaduje indexová stránka.

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

K otestování injektáže služby a nabídky v integračním testu se do testu vloží napodobená služba. Služba napodobení nahradí aplikaci QuoteService službou poskytovanou 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 služba s vymezeným oborem je zaregistrovaná:

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

Kód vytvořený během provádění testu odráží text uvozovek zadaný tímto výrazem TestQuoteService, a proto kontrolní výraz projde:

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

Napodobení ověřování

Testy ve AuthTests třídě kontrolují, že zabezpečený koncový bod:

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

V SUT stránka /SecurePage používá AuthorizePage konvenci k použití AuthorizeFilter stránky. Další informace najdete v tématu Razor Konvence autorizace stránek.

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

Get_SecurePageRedirectsAnUnauthenticatedUser V testu WebApplicationFactoryClientOptions je nastavena možnost zakázat přesměrování nastavením AllowAutoRedirect nafalse:

[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ž zakážete klientovi sledovat přesměrování, můžete provést následující kontroly:

  • Stavový kód vrácený SUT lze zkontrolovat podle očekávaného HttpStatusCode.Redirect výsledku, nikoli konečný stavový kód po přesměrování na přihlašovací stránku, což by bylo HttpStatusCode.OK.
  • Hodnota záhlaví v hlavičce odpovědi je zaškrtnutá, Location aby se ověřilo, že začíná http://localhost/Identity/Account/Login, ne poslední odpověď přihlašovací stránky, kde Location by záhlaví nebylo k dispozici.

Testovací aplikace může AuthenticationHandler<TOptions>ConfigureTestServices 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);
    }
}

Volá TestAuthHandler se k ověření uživatele, pokud je schéma ověřování nastaveno na Test místo, kde AddAuthentication je registrován ConfigureTestServices. Je důležité, Test aby schéma odpovídalo schématu, které vaše aplikace očekává. Jinak 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 najdete WebApplicationFactoryClientOptionsv části Možnosti klienta.

Nastavení prostředí

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

  • Nastavte proměnnou ASPNETCORE_ENVIRONMENT prostředí (například Staging, Productionnebo 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");

Způsob, jakým testovací infrastruktura odvodí kořenovou cestu obsahu aplikace

Konstruktor WebApplicationFactory odvodí kořenovou cestu obsahu aplikace tak, že vyhledá WebApplicationFactoryContentRootAttribute v sestavení obsahující integrační testy s klíčem, který se rovná TEntryPoint sestavení System.Reflection.Assembly.FullName. Pokud se atribut se správným klíčem nenajde, WebApplicationFactory vrátí se zpět do hledání souboru řešení (.sln) a připojí TEntryPoint název sestavení do adresáře řešení. Kořenový adresář aplikace (kořenová cesta k obsahu) slouží ke zjišťování zobrazení a souborů obsahu.

Zakázání stínové kopie

Stínová kopie způsobí, že testy se spustí v jiném adresáři než ve výstupním adresáři. Pokud vaše testy spoléhají na načítání souborů vzhledem k Assembly.Location problémům a dochází k problémům, možná budete muset stínové kopírování zakázat.

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

{
  "shadowCopy": false
}

Likvidace objektů

Po provedení testů IClassFixture implementace a HttpClient jsou uvolněny při xUnit disposes of the WebApplicationFactory. TestServer Pokud instance objektů vytvořených vývojářem vyžaduje odstranění, odstraňte je v implementaci IClassFixture . 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áv (SUT) src/RazorPagesProject Umožňuje uživateli přidávat, odstraňovat, odstraňovat všechny a analyzovat zprávy.
Testovací aplikace tests/RazorPagesProject.Tests Používá se k integraci testování SUT.

Testy je možné spouštět pomocí integrovaných testovacích funkcí integrovaného vývojového prostředí (IDE), například sady Visual Studio. Pokud používáte Visual Studio Code nebo příkazový řádek, spusťte v adresáři tests/RazorPagesProject.Tests následující příkaz:

dotnet test

Organizace aplikace zpráv (SUT)

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

  • Indexová stránka aplikace (Pages/Index.cshtml a Pages/Index.cshtml.cs) poskytuje uživatelské rozhraní a metody modelu stránky pro řízení sčítání, odstranění a analýzy zpráv (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í databáze v paměti entity Framework†.
  • Aplikace obsahuje vrstvu přístupu k datům (DAL) ve své třídě AppDbContext kontextu databáze (Data/AppDbContext.cs).
  • Pokud je databáze při spuštění aplikace prázdná, úložiště zpráv se inicializuje se třemi zprávami.
  • Aplikace zahrnuje /SecurePage přístup pouze ověřeným uživatelem.

†Pokudování pomocí nástroje InMemory vysvětluje, jak používat databázi v paměti pro testy s msTestem. Toto téma používá testovací architekturu xUnit . Koncepty testů a implementace testů v různých testovacích architekturách jsou podobné, ale ne identické.

I když aplikace nepoužívá vzor úložiště a není efektivním příkladem vzoruRazor Práce (UoW), stránky podporují tyto vzory vývoje. Další informace najdete v tématu Návrh vrstvy trvalosti infrastruktury a logiky kontroleru testů (ukázka implementuje vzor úložiště).

Testovací organizace aplikací

Testovací aplikace je konzolová aplikace uvnitř tests/RazorPagesProject.Tests adresáře.

Testovací adresář aplikace Popis
AuthTests Obsahuje testovací metody pro:
  • Přístup k zabezpečené stránce neověřeným uživatelem
  • Přístup k zabezpečené stránce ověřeným uživatelem pomocí napodobení AuthenticationHandler<TOptions>.
  • Získání profilu uživatele GitHubu a kontrola přihlášení uživatele profilu
BasicTests Obsahuje testovací metodu pro směrování a typ obsahu.
IntegrationTests Obsahuje integrační testy stránky indexu pomocí vlastní WebApplicationFactory třídy.
Helpers/Utilities
  • Utilities.cs obsahuje metodu použitou InitializeDbForTests k vytvoření databáze s 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 odesílání žádostí do SUT.

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

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

Ukázková aplikace vytvoří databázi se třemi zprávami v Utilities.cs tom, že testy můžou při spuštění použít:

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 registrován v jeho Startup.ConfigureServices metodě. Zpětné volání testovací aplikace builder.ConfigureServices se spustí po spuštění kódu aplikace Startup.ConfigureServices . Chcete-li pro testy použít jinou databázi, musí být kontext databáze aplikace nahrazen v builder.ConfigureServices. Další informace najdete v části Přizpůsobit webApplicationFactory .

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

Další materiály

Tento článek předpokládá základní znalosti testů jednotek. Pokud neznáte koncepty testů, podívejte se na článek o testování částí v .NET Core a .NET Standard a jeho propojeném obsahu.

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

Ukázková aplikace je Razor aplikace Pages a předpokládá základní znalost Razor stránek. Pokud stránky neznáte Razor , přečtěte si následující články:

Pro testování spA doporučujeme nástroj, jako je Playwright pro .NET, který můž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 například jednotlivé metody tříd. Testy integrace ověřují, že dvě nebo více součástí aplikace spolupracují a vytvářejí očekávaný výsledek, a to včetně všech komponent potřebných k úplnému zpracování požadavku.

Tyto širší testy se používají k otestová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 odpovědi požadavku

Testy jednotek používají prostředky infrastruktury, označované jako falešné nebo napodobené objekty, místo komponent infrastruktury.

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

  • Použijte skutečné komponenty, které aplikace používá v produkčním prostředí.
  • Vyžaduje více kódu a zpracování dat.
  • Spusťte delší dobu.

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

V diskusích o integračních testech se testovaný projekt často nazývá systém pod testem nebo "SUT". "SUT" se používá v tomto článku k odkazování na aplikaci ASP.NET Core, která se testuje.

Nezapisujte integrační testy pro každou 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ů, je obvykle schopná dostatečně testovat databáze a součásti systému souborů zaměřenou na čtení, zápis, aktualizaci a odstranění. Pro rutinní testy logiky metody, které s těmito komponentami pracují, použijte testy jednotek. Při testování jednotek může použití falešných nebo napodobených infrastruktur vést k rychlejšímu provádění testů.

testy integrace ASP.NET Core

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

  • Projekt testů se používá k zahrnutí a provádění testů. Testovací projekt má odkaz na SUT.
  • Testovací projekt 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.
  • Spouštěč testů se používá k provádění testů a hlášení výsledků testu.

Integrační testy se řídí posloupností událostí, které zahrnují obvyklé testovací kroky Uspořádat, Jednat a Assert :

  1. Webový hostitel SUT je nakonfigurovaný.
  2. Vytvoří se testovací serverový klient 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 actu : Klient odešle požadavek a obdrží odpověď.
  5. Provede se testovací krok Assert: Skutečnáodpověď se ověří jako úspěšné nebo neúspěšné na základě očekávané odpovědi.
  6. Proces pokračuje, dokud se všechny testy nespustí.
  7. Výsledky testu jsou hlášeny.

Obvykle je testovací webový hostitel 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 (TestServer), jsou poskytovány nebo spravovány balíčkem 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řen obsahu na kořen projektu SUT tak, aby se při spuštění testů zjistily statické soubory a stránky/zobrazení.
  • Poskytuje WebApplicationFactory třídy pro zjednodušení bootstrapping SUT s TestServer.

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

Oddělte testy jednotek od integračních testů do různých projektů. Oddělení testů:

  • Pomáhá zajistit, aby součásti testování infrastruktury nejsou omylem zahrnuty do testů jednotek.
  • Umožňuje kontrolu nad tím, jakou sadu testů se spouští.

Konfigurace pro testy Razor aplikací Pages a aplikací MVC není prakticky nijak rozdílná. Jediný rozdíl spočívá v tom, jak jsou testy pojmenovány. Razor V aplikaci Pages jsou testy koncových bodů stránky obvykle pojmenovány za třídou modelu stránky (například IndexPageTests pro testování integrace součástí pro indexovou stránku). V aplikaci MVC jsou testy obvykle uspořádány podle tříd kontroleru a pojmenovány podle kontrolerů, které testují (například HomeControllerTests pro testování integrace součástí pro Home kontroler).

Požadavky na testovací aplikaci

Testovací projekt musí:

Tyto požadavky se dají zobrazit v ukázkové aplikaci. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Zkontrolujte soubor. Ukázková aplikace používá testovací architekturu xUnit a knihovnu analyzátoru AngleSharp , takže ukázková aplikace také odkazuje:

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

Entity Framework Core se také používá v testech. Prohlédněte si soubor projektu na GitHubu.

Prostředí SUT

Pokud prostředí SUT není nastavené, prostředí se ve výchozím nastavení nastaví na Vývoj.

Základní testy s výchozím webapplicationFactory

Implicitně definovanou Program třídu zpřístupňte testovacímu projektu jedním z následujících způsobů:

  • Zveřejnění interních typů z webové aplikace do testovacího projektu To lze provést v souboru projektu SUT (.csproj):

    <ItemGroup>
         <InternalsVisibleTo Include="MyTestProject" />
    </ItemGroup>
    
  • Program Nastavení třídy jako veřejné pomocí částečné deklarace třídy:

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

    Ukázková aplikace používá Program přístup částečné třídy.

WebApplicationFactory<TEntryPoint> slouží k vytvoření TestServer integračních testů. TEntryPoint je vstupní bod třídy SUT, obvykle Program.cs.

Třídy testů implementují rozhraní příslušenství třídy (IClassFixture) k označení třídy obsahuje testy a poskytují sdílené instance objektů napříč testy ve třídě.

Následující testovací třída , BasicTestspoužívá WebApplicationFactory bootstrap SUT a poskytnout HttpClient testovací metodu, Get_EndpointsReturnSuccessAndCorrectContentType. Metoda ověří, že stavový kód odpovědi je úspěšný (200–299) a hlavička Content-Type je text/html; charset=utf-8 pro několik stránek aplikace.

CreateClient() vytvoří instanci HttpClient , která automaticky sleduje přesměrování a zpracovává cookies.

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

Ve výchozím nastavení se v případě povolení zásad souhlasu obecného nařízení o ochraně osobních údajů nezachovávají základní cookiepožadavky. Pokud chcete zachovat jiné než podstatné cookiehodnoty, například ty, které používá poskytovatel TempData, označte je v testech jako nezbytné. Pokyny k označení cookie jako nezbytné naleznete v tématu Základní cookies.

AngleSharp vs Application Parts . pro antiforgery kontroly

Tento článek používá analyzátor AngleSharp ke zpracování antiforgery kontroly načtením stránek a parsováním HTML. Pokud chcete otestovat koncové body zobrazení kontroleru a Razor stránek na nižší úrovni, aniž byste se museli zabývat tím, jak se vykreslují v prohlížeči, zvažte použití Application Parts. Přístup části aplikace vloží kontroler nebo Razor stránku do aplikace, které lze použít k provádění JSpožadavků ON, aby získaly požadované hodnoty. Další informace najdete v blogu o testování integrace ASP.NET základních prostředků chráněných antiforgery pomocí částí aplikace a přidruženého úložiště GitHub od Martin Costello.

Přizpůsobení služby WebApplicationFactory

Konfiguraci webového hostitele lze vytvořit nezávisle na testovacích třídách tak, že zdědíte WebApplicationFactory<TEntryPoint> vytvoření jedné nebo více vlastních továren:

  1. Dědit z WebApplicationFactory a přepsat ConfigureWebHost. Umožňuje IWebHostBuilder konfiguraci kolekce služeb pomocí 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");
        }
    }
    

    Počáteční databázová v ukázkové aplikaci se provádí metodou InitializeDbForTests . Metoda je popsaná v ukázce integračních testů: Část organizace testovací aplikace.

    Kontext databáze SUT je registrován v Program.cs. Zpětné volání testovací aplikace builder.ConfigureServices se spustí po spuštění kódu aplikace Program.cs . Chcete-li použít jinou databázi pro testy, než je databáze aplikace, musí být kontext databáze aplikace nahrazen v builder.ConfigureServices.

    Ukázková aplikace najde popisovač služby pro kontext databáze a pomocí popisovače odebere registraci služby. Továrna pak přidá novou ApplicationDbContext databázi v paměti pro testy..

    Pokud se chcete připojit k jiné databázi, změňte soubor DbConnection. Použití testovací databáze SQL Serveru:

  1. Použijte vlastní CustomWebApplicationFactory v testovacích třídách. Následující příklad používá továrnu IndexPageTests ve třídě:

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

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

  2. Typický test používá HttpClient metody a pomocné metody ke zpracování požadavku 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);
    }
    

Všechny požadavky POST na SUT musí splňovat antiforgery kontrolu, která je automaticky provedena systémem ochrany dat aplikace. Aby bylo možné zajistit požadavek POST testu, musí testovací aplikace:

  1. Vytvořte žádost o stránku.
  2. Parsujte antiforgery cookie a požadavek na ověřovací token z odpovědi.
  3. Vytvořte požadavek POST s použitím antiforgery cookie a ověřovacího tokenu požadavku.

Pomocné SendAsync rozšiřující metody (Helpers/HttpClientExtensions.cs) a GetDocumentAsync pomocná metoda (Helpers/HtmlHelpers.cs) v ukázkové aplikaci používají analyzátor AngleSharp ke zpracování kontroly antiforgery následujícími metodami:

  • GetDocumentAsync: Přijme HttpResponseMessage a vrátí hodnotu IHtmlDocument. GetDocumentAsync používá továrnu, která připraví virtuální odpověď na základě původní HttpResponseMessage. Další informace najdete v dokumentaci k sadě AngleSharp.
  • SendAsync metody rozšíření pro HttpClient vytvoření žádosti HttpRequestMessage a volání SendAsync(HttpRequestMessage) k odeslání žádostí do SUT. Přetížení pro SendAsync přijetí html formuláře (IHtmlFormElement) a následující:
    • Tlačítko Odeslat formulář (IHtmlElement)
    • Kolekce hodnot formulářů (IEnumerable<KeyValuePair<string, string>>)
    • Tlačítko Odeslat (IHtmlElement) a hodnoty formuláře (IEnumerable<KeyValuePair<string, string>>)

AngleSharp je knihovna analýzy třetí strany používaná pro demonstrační účely v tomto článku a ukázkové aplikaci. AngleSharp se nepodporuje ani nevyžaduje pro testování integrace aplikací ASP.NET Core. Další analyzátory je možné použít, například html agilní balíček (HAP). Dalším přístupem je napsat kód pro zpracování ověřovacího tokenu žádosti systému antiforgery a antiforgery cookie přímo. Další informace najdete v části AngleSharp vs Application Parts . antiforgery kontroly v tomto článku.

Zprostředkovatel databáze EF-Core v paměti lze použít pro omezené a základní testování, ale pro testování v paměti se doporučuje poskytovatel SQLite.

Viz Rozšíření po spuštění filtry , které ukazují, jak nakonfigurovat middleware pomocí IStartupFilter, což je užitečné, když test vyžaduje vlastní službu nebo middleware.

Přizpůsobení klienta pomocí withWebHostBuilder

Pokud je v rámci testovací metody vyžadována další konfigurace, WithWebHostBuilder vytvoří novou WebApplicationFactory konfiguraci IWebHostBuilder , která je dále přizpůsobena konfigurací.

Ukázkové volání WithWebHostBuilder kódu, která nahradí nakonfigurované služby testovacími zástupnými procedurami. Další informace a příklady použití najdete v části Vložení napodobených služeb v tomto článku.

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

Protož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 bude znovu provedena v této testovací metodě, aby se zajistilo, že záznam existuje pro SUT k odstranění. Výběr prvního tlačítka messages pro odstranění formuláře v SUT se simuluje v požadavku na SUT:

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

Možnosti klienta

Podívejte se na WebApplicationFactoryClientOptions stránku s výchozími a dostupnými možnostmi při vytváření HttpClient instancí.

WebApplicationFactoryClientOptions Vytvořte třídu a předejte ji metoděCreateClient():

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

POZNÁMKA: Pokud se chcete vyhnout upozorněním přesměrování HTTPS v protokolech při použití middlewaru přesměrování HTTPS, nastavte BaseAddress = new Uri("https://localhost")

Vložení napodobených služeb

Služby je možné přepsat v testu voláním ConfigureTestServices tvůrce hostitelů. K určení rozsahu přepsané služby na samotný test se WithWebHostBuilder metoda používá k načtení tvůrce hostitelů. To lze vidět v následujících testech:

Ukázková SUT obsahuje vymezenou službu, která vrací nabídku. Citace je vložena do skrytého pole na stránce Index, když se požaduje indexová stránka.

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

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

K otestování injektáže služby a nabídky v integračním testu se do testu vloží napodobená služba. Služba napodobení nahradí aplikaci QuoteService službou poskytovanou 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(
            "Something's interfering with time, Mr. Scarman, " +
            "and time is my business.");
    }
}

ConfigureTestServices je volána a služba s vymezeným oborem je zaregistrovaná:

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

Kód vytvořený během provádění testu odráží text uvozovek zadaný tímto výrazem TestQuoteService, a proto kontrolní výraz projde:

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

Napodobení ověřování

Testy ve AuthTests třídě kontrolují, že zabezpečený koncový bod:

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

V SUT stránka /SecurePage používá AuthorizePage konvenci k použití AuthorizeFilter stránky. Další informace najdete v tématu Razor Konvence autorizace stránek.

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

Get_SecurePageRedirectsAnUnauthenticatedUser V testu WebApplicationFactoryClientOptions je nastavena možnost zakázat přesměrování nastavením AllowAutoRedirect nafalse:

[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ž zakážete klientovi sledovat přesměrování, můžete provést následující kontroly:

  • Stavový kód vrácený SUT lze zkontrolovat podle očekávaného HttpStatusCode.Redirect výsledku, nikoli konečný stavový kód po přesměrování na přihlašovací stránku, což by bylo HttpStatusCode.OK.
  • Hodnota záhlaví v hlavičce odpovědi je zaškrtnutá, Location aby se ověřilo, že začíná http://localhost/Identity/Account/Login, ne poslední přihlašovací stránka odpovědi, kde Location by záhlaví nebylo k dispozici.

Testovací aplikace může AuthenticationHandler<TOptions>ConfigureTestServices 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, "TestScheme");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

Volá TestAuthHandler se k ověření uživatele, pokud je schéma ověřování nastaveno na TestScheme místo, kde AddAuthentication je registrován ConfigureTestServices. Je důležité, TestScheme aby schéma odpovídalo schématu, které vaše aplikace očekává. Jinak ověřování nebude fungovat.

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

Další informace najdete WebApplicationFactoryClientOptionsv části Možnosti klienta.

Základní testy pro middleware ověřování

Základní testy middlewaru ověřování najdete v tomto úložišti GitHubu. Obsahuje testovací server specifický pro testovací scénář.

Nastavení prostředí

Nastavte prostředí ve vlastní objektu pro vytváření aplikací:

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

Způsob, jakým testovací infrastruktura odvodí kořenovou cestu obsahu aplikace

Konstruktor WebApplicationFactory odvodí kořenovou cestu obsahu aplikace tak, že vyhledá WebApplicationFactoryContentRootAttribute v sestavení obsahující integrační testy s klíčem, který se rovná TEntryPoint sestavení System.Reflection.Assembly.FullName. Pokud se atribut se správným klíčem nenajde, WebApplicationFactory vrátí se zpět do hledání souboru řešení (.sln) a připojí TEntryPoint název sestavení do adresáře řešení. Kořenový adresář aplikace (kořenová cesta k obsahu) slouží ke zjišťování zobrazení a souborů obsahu.

Zakázání stínové kopie

Stínová kopie způsobí, že testy se spustí v jiném adresáři než ve výstupním adresáři. Pokud vaše testy spoléhají na načítání souborů vzhledem k Assembly.Location problémům a dochází k problémům, možná budete muset stínové kopírování zakázat.

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

{
  "shadowCopy": false
}

Likvidace objektů

Po provedení testů IClassFixture implementace a HttpClient jsou uvolněny při xUnit disposes of the WebApplicationFactory. TestServer Pokud instance objektů vytvořených vývojářem vyžaduje odstranění, odstraňte je v implementaci IClassFixture . 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áv (SUT) src/RazorPagesProject Umožňuje uživateli přidávat, odstraňovat, odstraňovat všechny a analyzovat zprávy.
Testovací aplikace tests/RazorPagesProject.Tests Používá se k integraci testování SUT.

Testy je možné spouštět pomocí integrovaných testovacích funkcí integrovaného vývojového prostředí (IDE), například sady Visual Studio. Pokud používáte Visual Studio Code nebo příkazový řádek, spusťte v adresáři tests/RazorPagesProject.Tests následující příkaz:

dotnet test

Organizace aplikace zpráv (SUT)

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

  • Indexová stránka aplikace (Pages/Index.cshtml a Pages/Index.cshtml.cs) poskytuje uživatelské rozhraní a metody modelu stránky pro řízení sčítání, odstranění a analýzy zpráv (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í databáze v paměti entity Framework†.
  • Aplikace obsahuje vrstvu přístupu k datům (DAL) ve své třídě AppDbContext kontextu databáze (Data/AppDbContext.cs).
  • Pokud je databáze při spuštění aplikace prázdná, úložiště zpráv se inicializuje se třemi zprávami.
  • Aplikace zahrnuje /SecurePage přístup pouze ověřeným uživatelem.

†Pokudování v článku EF, Test s InMemory, vysvětluje, jak používat databázi v paměti pro testy s MSTest. Toto téma používá testovací architekturu xUnit . Koncepty testů a implementace testů v různých testovacích architekturách jsou podobné, ale ne identické.

I když aplikace nepoužívá vzor úložiště a není efektivním příkladem vzoruRazor Práce (UoW), stránky podporují tyto vzory vývoje. Další informace najdete v tématu Návrh vrstvy trvalosti infrastruktury a logiky kontroleru testů (ukázka implementuje vzor úložiště).

Testovací organizace aplikací

Testovací aplikace je konzolová aplikace uvnitř tests/RazorPagesProject.Tests adresáře.

Testovací adresář aplikace Popis
AuthTests Obsahuje testovací metody pro:
  • Přístup k zabezpečené stránce neověřeným uživatelem
  • Přístup k zabezpečené stránce ověřeným uživatelem pomocí napodobení AuthenticationHandler<TOptions>.
  • Získání profilu uživatele GitHubu a kontrola přihlášení uživatele profilu
BasicTests Obsahuje testovací metodu pro směrování a typ obsahu.
IntegrationTests Obsahuje integrační testy stránky indexu pomocí vlastní WebApplicationFactory třídy.
Helpers/Utilities
  • Utilities.cs obsahuje metodu použitou InitializeDbForTests k vytvoření databáze s 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 odesílání žádostí do SUT.

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

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

Ukázková aplikace vytvoří databázi se třemi zprávami v Utilities.cs tom, že testy můžou při spuštění použít:

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 registrován v Program.cs. Zpětné volání testovací aplikace builder.ConfigureServices se spustí po spuštění kódu aplikace Program.cs . Chcete-li pro testy použít jinou databázi, musí být kontext databáze aplikace nahrazen v builder.ConfigureServices. Další informace najdete v části Přizpůsobit webApplicationFactory .

Další materiály