Testy integracji w ASP.NET Core

Przez Jos van der Til, Martin Costello i Javier Calvarro Nelson.

Testy integracji zapewniają prawidłowe działanie składników aplikacji na poziomie obejmującym infrastrukturę pomocniczą aplikacji, taką jak baza danych, system plików i sieć. ASP.NET Core obsługuje testy integracji przy użyciu struktury testów jednostkowych z testowym hostem internetowym i serwerem testowym w pamięci.

W tym artykule przyjęto założenie, że podstawowa wiedza na temat testów jednostkowych. Jeśli nie masz pewności co do pojęć związanych z testowaniem, zobacz artykuł Unit Testing in .NET Core and .NET Standard (Testowanie jednostkowe na platformie .NET Core i .NET Standard ) oraz jego zawartość połączona.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

Przykładowa aplikacja to Razor aplikacja Pages i zakłada podstawową wiedzę na temat Razor stron. Jeśli nie Razor znasz stron, zobacz następujące artykuły:

Do testowania spAs zalecamy narzędzie takie jak Playwright dla platformy .NET, które może zautomatyzować przeglądarkę.

Wprowadzenie do testów integracji

Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klas. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą w celu uzyskania oczekiwanego wyniku, prawdopodobnie w tym każdego składnika wymaganego do pełnego przetworzenia żądania.

Te szersze testy służą do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:

  • baza danych
  • System plików
  • Urządzenia sieciowe
  • Potok odpowiedzi na żądanie

Testy jednostkowe używają składników sfabryzowanych, znanych jako fałszywe lub pozorowane obiekty, zamiast składników infrastruktury.

W przeciwieństwie do testów jednostkowych testy integracji:

  • Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
  • Wymagaj więcej kodu i przetwarzania danych.
  • Uruchamianie trwa dłużej.

W związku z tym należy ograniczyć użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.

W dyskusjach na temat testów integracji testowany projekt jest często nazywany systemowym testem lub "SUT" krótko. Funkcja "SUT" jest używana w tym artykule w celu odwoływania się do testowanej aplikacji ASP.NET Core.

Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od tego, ile miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania zwykle umożliwia odpowiednie testowanie składników bazy danych i systemu plików. Użyj testów jednostkowych do rutynowych testów logiki metody, które wchodzą w interakcję z tymi składnikami. W testach jednostkowych użycie podróbek infrastruktury lub makiety skutkuje szybszym wykonywaniem testów.

testy integracji ASP.NET Core

Testy integracji w ASP.NET Core wymagają następujących elementów:

  • Projekt testowy jest używany do przechowywania i wykonywania testów. Projekt testowy zawiera odwołanie do sut.
  • Projekt testowy tworzy testowy host internetowy dla sut i używa klienta serwera testowego do obsługi żądań i odpowiedzi za pomocą sut.
  • Moduł uruchamiający testy służy do wykonywania testów i zgłaszania wyników testu.

Testy integracji są zgodne z sekwencją zdarzeń obejmujących zwykłe kroki testu Rozmieszczanie, Act i Assert :

  1. Skonfigurowano hosta internetowego sut.
  2. Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
  3. Krok rozmieszczania testu jest wykonywany: aplikacja testowa przygotowuje żądanie.
  4. Krok testu act jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.
  5. Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako powodzenie lub niepowodzenie na podstawie oczekiwanej odpowiedzi.
  6. Proces jest kontynuowany do momentu wykonania wszystkich testów.
  7. Wyniki testów są zgłaszane.

Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji dla przebiegów testu. Na przykład do testów może być używana inna baza danych lub inne ustawienia aplikacji.

Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są dostarczane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Korzystanie z tego pakietu usprawnia tworzenie i wykonywanie testów.

Pakiet Microsoft.AspNetCore.Mvc.Testing obsługuje następujące zadania:

  • Kopiuje plik zależności (.deps) z sut do katalogu projektu testowego bin .
  • Ustawia katalog główny zawartości na katalog główny projektu SUT, tak aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
  • Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie przy użyciu narzędzia SUT za pomocą polecenia TestServer.

W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy wraz ze szczegółowymi instrukcjami dotyczącymi uruchamiania testów i zaleceń dotyczących nazywania testów i klas testowych.

Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:

  • Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
  • Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.

Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyna różnica polega na tym, jak są nazwane testy. Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane na podstawie kontrolerów testowych (na przykład HomeControllerTests w celu przetestowania integracji składników dla Home kontrolera).

Testowanie wymagań wstępnych aplikacji

Projekt testowy musi:

Te wymagania wstępne można zobaczyć w przykładowej aplikacji. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Sprawdź plik. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:

W aplikacjach korzystających z xunit.runner.visualstudio wersji 2.4.2 lub nowszej projekt testowy musi odwoływać się do Microsoft.NET.Test.Sdk pakietu.

Program Entity Framework Core jest również używany w testach. Zobacz plik projektu w usłudze GitHub.

Środowisko SUT

Jeśli środowisko SUT nie jest ustawione, środowisko jest domyślnie ustawione na Programowanie.

Podstawowe testy z domyślną funkcją WebApplicationFactory

Uwidacznia niejawnie zdefiniowaną Program klasę w projekcie testowym, wykonując jedną z następujących czynności:

  • Uwidacznia typy wewnętrzne z aplikacji internetowej w projekcie testowym. Można to zrobić w pliku projektu SUT (.csproj):

    <ItemGroup>
         <InternalsVisibleTo Include="MyTestProject" />
    </ItemGroup>
    
  • Ustaw klasęProgram jako publiczną przy użyciu deklaracji częściowej klasy:

    var builder = WebApplication.CreateBuilder(args);
    // ... Configure services, routes, etc.
    app.Run();
    + public partial class Program { }
    
    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());
        }
    }
    

    Przykładowa aplikacja korzysta z Program metody klasy częściowej.

WebApplicationFactory<TEntryPoint> służy do tworzenia elementu TestServer dla testów integracji. TEntryPoint jest klasą punktu wejścia sut, zwykle Program.cs.

Klasy testowe implementują interfejs oprawy klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wystąpienia obiektów udostępnionych w testach w klasie.

Następująca klasa BasicTeststestowa , używa WebApplicationFactory metody , aby uruchomić sut i podać metodę HttpClient testową . Get_EndpointsReturnSuccessAndCorrectContentType Metoda sprawdza, czy kod stanu odpowiedzi zakończył się pomyślnie (200–299), a Content-Type nagłówek dotyczy text/html; charset=utf-8 kilku stron aplikacji.

CreateClient() Tworzy wystąpienie tego HttpClient obiektu automatycznie podąża za przekierowaniami i obsługuje 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());
    }
}

Domyślnie ustawienia inne niż podstawowe cookienie są zachowywane w żądaniach po włączeniu zasad zgody ogólnego rozporządzenia o ochronie danych . Aby zachować nieistniejących cookieelementów, takich jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Essential cookies.

AngleSharp a Application Parts kontrole antyforgery

W tym artykule użyto analizatora AngleSharp do obsługi testów antyforgeryjnych przez ładowanie stron i analizowanie kodu HTML. Aby przetestować punkty końcowe widoków kontrolera i Razor stron na niższym poziomie, bez dbania o sposób ich renderowania w przeglądarce, rozważ użycie polecenia Application Parts. Metoda Części aplikacji wprowadza kontroler lub Razor stronę do aplikacji, która może służyć do JSwprowadzania żądań WŁ. w celu uzyskania wymaganych wartości. Aby uzyskać więcej informacji, zobacz blog Integration Testing ASP.NET Core Resources Protected with Antiforgery Using Application Part and associated GitHub repo by Martin Costello (Testowanie integracji ASP.NET Core zasobów chronionych przy użyciu składników aplikacji przy użyciu części aplikacji i skojarzonego repozytorium GitHub firmy Martin Costello).

Dostosowywanie elementu WebApplicationFactory

Konfigurację hosta internetowego można utworzyć niezależnie od klas testowych, dziedzicząc od WebApplicationFactory<TEntryPoint> , aby utworzyć co najmniej jedną fabrykę niestandardową:

  1. Dziedzicz z WebApplicationFactory i przesłoń ConfigureWebHost. Umożliwia IWebHostBuilder konfigurację kolekcji usług za pomocą polecenia IWebHostBuilder.ConfigureServices

    public class CustomWebApplicationFactory<TProgram>
        : WebApplicationFactory<TProgram> where TProgram : 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");
                });
            });
    
            builder.UseEnvironment("Development");
        }
    }
    

    Rozmieszczanie bazy danych w przykładowej aplikacji jest wykonywane przez metodę InitializeDbForTests . Metoda została opisana w sekcji Testy integracji: Testowanie organizacji aplikacji .

    Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych dla testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.

    Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nową ApplicationDbContext bazę danych, która używa bazy danych w pamięci dla testów.

    Aby nawiązać połączenie z inną bazą danych niż baza danych w pamięci, zmień UseInMemoryDatabase wywołanie, aby połączyć kontekst z inną bazą danych. Aby użyć SQL Server testowej bazy danych:

    public class CustomWebApplicationFactory<TProgram>
        : WebApplicationFactory<TProgram> where TProgram : 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");
                });
            });
    
            builder.UseEnvironment("Development");
        }
    }
    
  2. Użyj niestandardowego CustomWebApplicationFactory w klasach testowych. W poniższym przykładzie użyto fabryki IndexPageTests w klasie:

    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 przykładowej aplikacji jest skonfigurowany tak, aby uniemożliwić HttpClient wykonanie następujących przekierowań. Jak wyjaśniono w dalszej części sekcji Uwierzytelnianie mock , pozwala to testom sprawdzić wynik pierwszej odpowiedzi aplikacji. Pierwsza odpowiedź to przekierowanie w wielu z tych testów z nagłówkiem Location .

  3. Typowy test używa HttpClient metod pomocników i do przetwarzania żądania i odpowiedzi:

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

Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:

  1. Prześlij żądanie dotyczące strony.
  2. Przeanalizuj token antyforgery cookie i zażądaj weryfikacji z odpowiedzi.
  3. Utwórz żądanie POST przy użyciu tokenu sprawdzania poprawności żądań i ochrony przed błędem cookie .

SendAsync Metody rozszerzenia pomocnika () i GetDocumentAsync metoda pomocnika (Helpers/HttpClientExtensions.csHelpers/HtmlHelpers.cs) w przykładowej aplikacji używają analizatora AngleSharp do obsługi sprawdzania antyforgery przy użyciu następujących metod:

  • GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca element IHtmlDocument. GetDocumentAsync używa fabryki, która przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
  • SendAsync metody rozszerzenia dla HttpClient tworzenia i HttpRequestMessage wywołania SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia dla SendAsync akceptowania formularza HTML (IHtmlFormElement) i następujących:
    • Przycisk Prześlij formularz (IHtmlElement)
    • Kolekcja wartości formularzy (IEnumerable<KeyValuePair<string, string>>)
    • Przycisk przesyłania (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)

AngleSharp to biblioteka analizowania innych firm używana do celów demonstracyjnych w tym artykule i przykładowej aplikacji. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Inne analizatory mogą być używane, takie jak Pakiet Zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu antyforgeryjnego i bezpośredniego ochrony przed fałszerzami cookie . Aby uzyskać więcej informacji, zobacz AngleSharp vs Application Parts for antiforgery checks in this article (Kontrola kątowa w porównaniu z sprawdzaniem ochrony przed wybrykami ).

Dostawca bazy danych EF-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.

Dostosowywanie klienta za pomocą elementu WithWebHostBuilder

Jeśli wymagana jest dodatkowa konfiguracja w metodzie testowej, WithWebHostBuilder tworzy nową WebApplicationFactory z elementem IWebHostBuilder , który jest jeszcze bardziej dostosowany przez konfigurację.

Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji pokazuje użycie metody WithWebHostBuilder. Ten test wykonuje usuwanie rekordów w bazie danych przez wyzwolenie przesyłania formularza w sut.

Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych zostanie ponownie przesiecona w tej metodzie testowej, aby upewnić się, że rekord jest obecny dla sut do usunięcia. Wybranie pierwszego przycisku messages usuwania formularza w sut jest symulowane w żądaniu do 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);
}

Opcje klienta

Zobacz stronę, aby WebApplicationFactoryClientOptions uzyskać opcje domyślne i dostępne podczas tworzenia HttpClient wystąpień.

Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody:

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

Wstrzykiwanie makiety usług

Usługi można przesłonić w teście za pomocą wywołania do ConfigureTestServices konstruktora hosta.

Przykładowy kod SUT zawiera usługę o określonym zakresie, która zwraca cudzysłów. Cudzysłów jest osadzony w ukrytym polu na stronie Indeks po zażądaniu strony Indeks.

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

Następujące adiustacja jest generowana po uruchomieniu aplikacji SUT:

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

Aby przetestować usługę i zacytować iniekcję w teście integracji, przez test jest wstrzykiwana pozorowana usługa. Pozorna usługa zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie 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 jest wywoływana, a usługa o określonym zakresie jest zarejestrowana:

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

Znacznik wygenerowany podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez TestQuoteServiceusługę , co oznacza, że asercji przechodzi:

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

Pozorowanie uwierzytelniania

Testy w AuthTests klasie sprawdzają, czy bezpieczny punkt końcowy:

  • Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.
  • Zwraca zawartość uwierzytelnionego użytkownika.

Na stronie SUT jest /SecurePage używana AuthorizePage konwencja stosowania elementu AuthorizeFilter do strony. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.

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

W teście Get_SecurePageRedirectsAnUnauthenticatedUser ustawiono WebApplicationFactoryClientOptions opcję nie zezwalaj na przekierowania, ustawiając wartość AllowAutoRedirect na false:

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

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

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

Nie zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące testy:

  • Kod stanu zwrócony przez sut można sprawdzić względem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowy kod stanu po przekierowaniu do strony logowania, co byłoby .HttpStatusCode.OK
  • Wartość Location nagłówka w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie ostatecznej odpowiedzi na stronę logowania, gdzie Location nagłówek nie będzie obecny.

Aplikacja testowa może wyśmiewać element AuthenticationHandler<TOptions> w ConfigureTestServices celu przetestowania aspektów uwierzytelniania i autoryzacji. Minimalny scenariusz zwraca wartość :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);
    }
}

Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy schemat uwierzytelniania jest ustawiony na Test miejsce, w którym AddAuthentication jest zarejestrowany dla ConfigureTestServiceselementu . Ważne jest Test , aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.

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

Aby uzyskać więcej informacji na WebApplicationFactoryClientOptionstemat programu , zobacz sekcję Opcje klienta .

Podstawowe testy oprogramowania pośredniczącego uwierzytelniania

Zobacz to repozytorium GitHub, aby zapoznać się z podstawowymi testami oprogramowania pośredniczącego uwierzytelniania. Zawiera on serwer testowy specyficzny dla scenariusza testowego.

Ustawianie środowiska

Ustaw środowisko w niestandardowej fabryce aplikacji:

public class CustomWebApplicationFactory<TProgram>
    : WebApplicationFactory<TProgram> where TProgram : 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");
            });
        });

        builder.UseEnvironment("Development");
    }
}

Jak infrastruktura testowa wywnioskuje ścieżkę główną zawartości aplikacji

Konstruktor WebApplicationFactory wywnioskuje ścieżkę główną zawartości aplikacji, wyszukując element WebApplicationFactoryContentRootAttribute w zestawie zawierającym testy integracji z kluczem równym zestawowi TEntryPointSystem.Reflection.Assembly.FullName. Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory powraca do wyszukiwania pliku rozwiązania (sln) i dołącza TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.

Wyłączanie kopiowania w tle

Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotkaniu problemów, może być konieczne wyłączenie kopiowania w tle.

Aby wyłączyć kopiowanie w tle podczas korzystania z narzędzia xUnit, utwórz xunit.runner.json plik w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:

{
  "shadowCopy": false
}

Usuwanie obiektów

Po wykonaniu IClassFixtureTestServer testów implementacji zostaną HttpClient usunięte, gdy xUnit usuwa element WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy usunąć je w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Implementowanie metody Dispose.

Przykład testów integracji

Przykładowa aplikacja składa się z dwóch aplikacji:

Aplikacja Katalog projektu Opis
Aplikacja komunikatów (SUT) src/RazorPagesProject Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Aplikacja testowa tests/RazorPagesProject.Tests Służy do testowania integracji sut.

Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:

dotnet test

Organizacja aplikacji wiadomości (SUT)

Sut to Razor system komunikatów stron o następujących cechach:

  • Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) udostępnia metody interfejsu użytkownika i modelu strony do kontrolowania dodawania, usuwania i analizy komunikatów (średnie wyrazy na komunikat).
  • Komunikat jest opisany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (komunikat). Właściwość jest wymagana Text i ograniczona do 200 znaków.
  • Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
  • Aplikacja zawiera warstwę dostępu do danych (DAL) w klasie AppDbContext kontekstowej bazy danych (Data/AppDbContext.cs).
  • Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
  • Aplikacja zawiera element /SecurePage , do którego można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.

† Artykuł EF, Test with InMemory, wyjaśnia, jak używać bazy danych w pamięci do testów przy użyciu biblioteki MSTest. W tym temacie jest używana struktura testowa xUnit . Pojęcia testowe i implementacje testów w różnych platformach testowych są podobne, ale nie identyczne.

Chociaż aplikacja nie używa wzorca repozytorium i nie jest skutecznym przykładem wzorca Unit of Work (UoW),Razor strony obsługują te wzorce programowania. Aby uzyskać więcej informacji, zobacz Projektowanie warstwy trwałości infrastruktury i logiki kontrolera testów (przykład implementuje wzorzec repozytorium).

Testowanie organizacji aplikacji

Aplikacja testowa to aplikacja konsolowa wewnątrz tests/RazorPagesProject.Tests katalogu.

Testowanie katalogu aplikacji Opis
AuthTests Zawiera metody testowe dla:
  • Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
  • Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika za pomocą makiety AuthenticationHandler<TOptions>.
  • Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika profilu.
BasicTests Zawiera metodę testową routingu i typu zawartości.
IntegrationTests Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
  • Utilities.cs zawiera metodę InitializeDbForTests używaną do rozmieszczania bazy danych z danymi testowymi.
  • HtmlHelpers.cs zapewnia metodę zwracania elementu AngleSharp IHtmlDocument do użycia przez metody testowe.
  • HttpClientExtensions.cs zapewniają przeciążenia do SendAsync przesyłania żądań do sut.

Struktura testowa to xUnit. Testy integracji są przeprowadzane przy użyciu elementu Microsoft.AspNetCore.TestHost, który zawiera TestServerelement . Microsoft.AspNetCore.Mvc.Testing Ponieważ pakiet jest używany do konfigurowania hosta testowego i serwera testowego, TestHost pakiety i TestServer nie wymagają odwołań do pakietów bezpośrednich w pliku projektu aplikacji testowej ani konfiguracji dewelopera w aplikacji testowej.

Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.

Przykładowa aplikacja wysiewuje bazę danych z trzema komunikatami w Utilities.cs tych testach, które mogą być używane podczas wykonywania:

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

Kontekst bazy danych SUT jest zarejestrowany w pliku Program.cs. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu kodu aplikacji Program.cs . Aby użyć innej bazy danych dla testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Customize WebApplicationFactory (Dostosowywanie elementu WebApplicationFactory ).

Dodatkowe zasoby

W tym temacie założono podstawową wiedzę na temat testów jednostkowych. Jeśli nie ma pojęcia dotyczące testów, zobacz temat Unit Testing in .NET Core and .NET Standard (Testowanie jednostkowe na platformie .NET Core i .NET Standard ) oraz jego połączoną zawartość.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

Przykładowa aplikacja to Razor aplikacja Pages i zakłada podstawową wiedzę na temat Razor stron. Jeśli nie masz pewności co do stron, zapoznaj się z Razor następującymi tematami:

Uwaga

Do testowania spAs zalecamy narzędzie, takie jak Playwright dla platformy .NET, które może zautomatyzować przeglądarkę.

Wprowadzenie do testów integracji

Testy integracji oceniają składniki aplikacji na szerszym poziomie niż testy jednostkowe. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klas. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą, aby wygenerować oczekiwany wynik, prawdopodobnie w tym każdy składnik wymagany do pełnego przetworzenia żądania.

Te szersze testy są używane do testowania infrastruktury aplikacji i całej struktury, często w tym następujących składników:

  • baza danych
  • System plików
  • Urządzenia sieciowe
  • Potok odpowiedzi na żądanie

Testy jednostkowe używają sfabrykowanych składników, znanych jako fałszywe lub pozorowaneobiekty, zamiast składników infrastruktury.

W przeciwieństwie do testów jednostkowych testy integracji:

  • Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.
  • Wymagaj więcej kodu i przetwarzania danych.
  • Uruchamianie trwa dłużej.

W związku z tym należy ograniczyć użycie testów integracji do najważniejszych scenariuszy infrastruktury. Jeśli zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.

W dyskusjach na temat testów integracji testowany projekt jest często nazywany systemem w ramach testu lub "SUT" na krótko. Wyrażenie "SUT" jest używane w tym artykule w celu odwoływania się do testowanej aplikacji ASP.NET Core.

Nie zapisuj testów integracji dla każdej permutacji danych i dostępu do plików za pomocą baz danych i systemów plików. Niezależnie od tego, ile miejsc w aplikacji wchodzi w interakcje z bazami danych i systemami plików, skoncentrowany zestaw testów integracji odczytu, zapisu, aktualizacji i usuwania jest zwykle zdolny do odpowiedniego testowania składników bazy danych i systemu plików. Użyj testów jednostkowych do rutynowych testów logiki metody, które współdziałają z tymi składnikami. W testach jednostkowych użycie fałszywych lub pozorów infrastruktury powoduje szybsze wykonywanie testów.

testy integracji ASP.NET Core

Testy integracji w ASP.NET Core wymagają następujących elementów:

  • Projekt testowy służy do zawierania i wykonywania testów. Projekt testowy zawiera odwołanie do sut.
  • Projekt testowy tworzy testowy host internetowy dla sut i używa klienta serwera testowego do obsługi żądań i odpowiedzi za pomocą sut.
  • Moduł uruchamiający testy służy do wykonywania testów i zgłaszania wyników testu.

Testy integracji są zgodne z sekwencją zdarzeń, które obejmują zwykłe kroki testowe Rozmieszczanie, Działanie i Asercja :

  1. Host internetowy SUT jest skonfigurowany.
  2. Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.
  3. Krok rozmieszczania testu jest wykonywany: aplikacja testowa przygotowuje żądanie.
  4. Krok testu działania jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.
  5. Krok testu asertywnego jest wykonywany: rzeczywista odpowiedź jest weryfikowana jako przekazywanie lub niepowodzenie w oparciu o oczekiwaną odpowiedź.
  6. Proces jest kontynuowany do momentu wykonania wszystkich testów.
  7. Wyniki testów są zgłaszane.

Zazwyczaj testowy host internetowy jest skonfigurowany inaczej niż normalny host internetowy aplikacji dla przebiegów testów. Na przykład inna baza danych lub inne ustawienia aplikacji mogą być używane do testów.

Składniki infrastruktury, takie jak testowy host internetowy i serwer testowy w pamięci (TestServer), są udostępniane lub zarządzane przez pakiet Microsoft.AspNetCore.Mvc.Testing . Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.

Pakiet Microsoft.AspNetCore.Mvc.Testing obsługuje następujące zadania:

  • Kopiuje plik zależności (.deps) z sut do katalogu projektu testowego bin .
  • Ustawia katalog główny zawartości katalogu głównego projektu SUT, aby pliki statyczne i strony/widoki zostały znalezione podczas wykonywania testów.
  • Udostępnia klasę WebApplicationFactory , aby usprawnić uruchamianie przy użyciu narzędzia SUT za pomocą polecenia TestServer.

W dokumentacji testów jednostkowych opisano sposób konfigurowania projektu testowego i modułu uruchamiającego testy wraz ze szczegółowymi instrukcjami dotyczącymi uruchamiania testów i zaleceń dotyczących nazywania testów i klas testowych.

Oddziel testy jednostkowe od testów integracji do różnych projektów. Oddzielanie testów:

  • Pomaga zagwarantować, że składniki testowania infrastruktury nie zostaną przypadkowo uwzględnione w testach jednostkowych.
  • Umożliwia kontrolę nad tym, który zestaw testów jest uruchamiany.

Praktycznie nie ma różnicy między konfiguracją testów Razor aplikacji Pages i aplikacji MVC. Jedyna różnica polega na tym, jak są nazwane testy. Razor W aplikacji Pages testy punktów końcowych strony są zwykle nazwane po klasie modelu strony (na przykład IndexPageTests w celu przetestowania integracji składników dla strony Indeks). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolerów i nazwane na podstawie kontrolerów testowych (na przykład HomeControllerTests w celu przetestowania integracji składników dla Home kontrolera).

Testowanie wymagań wstępnych aplikacji

Projekt testowy musi:

Te wymagania wstępne można zobaczyć w przykładowej aplikacji. tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj Sprawdź plik. Przykładowa aplikacja używa struktury testowej xUnit i biblioteki analizatora AngleSharp , więc przykładowa aplikacja również odwołuje się do:

W aplikacjach korzystających z xunit.runner.visualstudio wersji 2.4.2 lub nowszej projekt testowy musi odwoływać się do Microsoft.NET.Test.Sdk pakietu.

Program Entity Framework Core jest również używany w testach. Odwołania do aplikacji:

Środowisko SUT

Jeśli środowisko SUT nie jest ustawione, środowisko jest domyślnie ustawione na Programowanie.

Podstawowe testy z domyślną funkcją WebApplicationFactory

ASP.NET Core 6 wprowadzonoWebApplication, które usunęły potrzebę klasyStartup. Aby przetestować WebApplicationFactory klasę Startup bez klasy, aplikacja ASP.NET Core 6 musi uwidocznić niejawnie zdefiniowaną Program klasę w projekcie testowym, wykonując jedną z następujących czynności:

  • Uwidacznia typy wewnętrzne z aplikacji internetowej w projekcie testowym. Można to zrobić w pliku projektu (.csproj):
    <ItemGroup>
         <InternalsVisibleTo Include="MyTestProject" />
    </ItemGroup>
    
  • Ustaw klasę Program jako publiczną przy użyciu deklaracji częściowej klasy:
    var builder = WebApplication.CreateBuilder(args);
    // ... Configure services, routes, etc.
    app.Run();
    + public partial class Program { }
    

Po wprowadzeniu zmian w aplikacji internetowej projekt testowy może teraz używać Program klasy dla klasy WebApplicationFactory.

[Fact]
public async Task HelloWorldTest()
{
    var application = new WebApplicationFactory<Program>()
        .WithWebHostBuilder(builder =>
        {
            // ... Configure test services
        });

    var client = application.CreateClient();
    //...
}

WebApplicationFactory<TEntryPoint> służy do tworzenia elementu TestServer dla testów integracji. TEntryPoint jest klasą punktu wejścia sut, zwykle klasą Startup .

Klasy testowe implementują interfejs oprawy klasy (IClassFixture), aby wskazać, że klasa zawiera testy i udostępnia wystąpienia obiektów udostępnionych w testach w klasie.

Następująca klasa BasicTeststestowa , używa WebApplicationFactory metody , aby uruchomić sut i podać metodę HttpClient testową . Get_EndpointsReturnSuccessAndCorrectContentType Metoda sprawdza, czy kod stanu odpowiedzi zakończył się pomyślnie (kody stanu w zakresie od 200 do 299), a Content-Type nagłówek dotyczy text/html; charset=utf-8 kilku stron aplikacji.

CreateClient() Tworzy wystąpienie tego HttpClient obiektu automatycznie podąża za przekierowaniami i obsługuje 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());
    }
}

Domyślnie nie są cookiezachowywane między żądaniami, gdy zasady zgody RODO są włączone. Aby zachować nieistniejących cookieelementów, takich jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach. Aby uzyskać instrukcje dotyczące oznaczania cookie elementu jako niezbędnego, zobacz Essential cookies.

Dostosowywanie elementu WebApplicationFactory

Konfigurację hosta sieci Web można utworzyć niezależnie od klas testowych przez dziedziczenie z WebApplicationFactory w celu utworzenia co najmniej jednej fabryki niestandardowej:

  1. Dziedzicz z WebApplicationFactory i przesłaniaj ConfigureWebHost. Element IWebHostBuilder umożliwia konfigurację kolekcji usług za pomocą polecenia 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);
                    }
                }
            });
        }
    }
    

    Rozmieszczanie bazy danych w przykładowej aplikacji jest wykonywane przez metodę InitializeDbForTests . Metoda jest opisana w sekcji Przykładowe testy integracji: Testowanie organizacji aplikacji .

    Kontekst bazy danych SUT jest zarejestrowany w jego Startup.ConfigureServices metodzie. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu Startup.ConfigureServices kodu aplikacji. Kolejność wykonywania to zmiana powodująca niezgodność dla hosta ogólnego z wydaniem ASP.NET Core 3.0. Aby użyć innej bazy danych do testów niż baza danych aplikacji, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices.

    W przypadku jednostek UWIERZYTELNIANIA, które nadal korzystają z hosta sieci Web, wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane przed kodem SUT Startup.ConfigureServices . Wywołanie zwrotne aplikacji testowej builder.ConfigureTestServices jest wykonywane po.

    Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi. Następnie fabryka dodaje nową ApplicationDbContext bazę danych, która używa bazy danych w pamięci do testów.

    Aby nawiązać połączenie z inną bazą danych niż baza danych w pamięci, zmień UseInMemoryDatabase wywołanie , aby połączyć kontekst z inną bazą danych. Aby użyć testowej bazy danych SQL Server:

    services.AddDbContext<ApplicationDbContext>((options, context) => 
    {
        context.UseSqlServer(
            Configuration.GetConnectionString("TestingDbConnectionString"));
    });
    
  2. Użyj niestandardowego CustomWebApplicationFactory w klasach testowych. W poniższym przykładzie użyto fabryki IndexPageTests w klasie :

    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 przykładowej aplikacji jest skonfigurowany tak, aby zapobiec HttpClient następującym przekierowaniom. Jak wyjaśniono w dalszej części sekcji Uwierzytelnianie mock , pozwala to testom sprawdzić wynik pierwszej odpowiedzi aplikacji. Pierwsza odpowiedź to przekierowanie w wielu z tych testów z nagłówkiem Location .

  3. Typowy test używa HttpClient metod i pomocnika do przetwarzania żądania i odpowiedzi:

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

Każde żądanie POST do SUT musi spełniać kontrolę antyforgeryjną, która jest automatycznie dokonana przez system ochrony danych aplikacji. Aby zorganizować żądanie POST testu, aplikacja testowa musi:

  1. Utwórz żądanie dla strony.
  2. Przeanalizuj antyforgeryjność cookie i zażądaj tokenu weryfikacji z odpowiedzi.
  3. Utwórz żądanie POST przy użyciu tokenu antyforgery i cookie żądania weryfikacji.

SendAsync Metody rozszerzenia pomocnika (Helpers/HttpClientExtensions.cs) i GetDocumentAsync metoda pomocnika (Helpers/HtmlHelpers.cs) w przykładowej aplikacji używają analizatora AngleSharp do obsługi sprawdzania antyforgery przy użyciu następujących metod:

  • GetDocumentAsync: Odbiera element HttpResponseMessage i zwraca wartość IHtmlDocument. GetDocumentAsync używa fabryki, która przygotowuje wirtualną odpowiedź na podstawie oryginalnego HttpResponseMessage. Aby uzyskać więcej informacji, zobacz dokumentację AngleSharp.
  • SendAsync metody rozszerzenia dla redagowania HttpClient i HttpRequestMessage wywołania SendAsync(HttpRequestMessage) w celu przesłania żądań do SUT. Przeciążenia do SendAsync akceptowania formularza HTML (IHtmlFormElement) i następującego:
    • Przycisk Prześlij formularza (IHtmlElement)
    • Kolekcja wartości formularzy (IEnumerable<KeyValuePair<string, string>>)
    • Przycisk przesyłania (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)

Uwaga

AngleSharp to biblioteka analizy innej firmy używana do celów demonstracyjnych w tym temacie i przykładowej aplikacji. Funkcja AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core. Można użyć innych analizatorów, takich jak pakiet zwinności HTML (HAP). Innym podejściem jest napisanie kodu do obsługi tokenu weryfikacji żądania systemu ochrony przed fałszerzami i bezpośredniego pisania cookie .

Uwaga

Dostawca bazy danych ef-Core w pamięci może służyć do ograniczonego i podstawowego testowania, jednak dostawca SQLite jest zalecanym wyborem do testowania w pamięci.

Dostosowywanie klienta za pomocą obiektu WithWebHostBuilder

Jeśli dodatkowa konfiguracja jest wymagana w ramach metody testowej, WithWebHostBuilder program tworzy nowy WebApplicationFactory element z elementem IWebHostBuilder , który jest jeszcze bardziej dostosowany przez konfigurację.

Metoda Post_DeleteMessageHandler_ReturnsRedirectToRoot testowa przykładowej aplikacji demonstruje użycie metody WithWebHostBuilder. Ten test wykonuje usuwanie rekordów w bazie danych przez wyzwolenie przesyłania formularza w pliku SUT.

Ponieważ inny test w IndexPageTests klasie wykonuje operację, która usuwa wszystkie rekordy w bazie danych i może zostać uruchomiona przed Post_DeleteMessageHandler_ReturnsRedirectToRoot metodą, baza danych jest ponownie przesyłana w tej metodzie testowej, aby upewnić się, że rekord jest obecny dla sut do usunięcia. Wybranie pierwszego przycisku messages usunięcia formularza w sut jest symulowane w żądaniu do 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);
}

Opcje klienta

W poniższej tabeli przedstawiono domyślne WebApplicationFactoryClientOptions dostępne podczas tworzenia HttpClient wystąpień.

Opcja Opis Domyślny
AllowAutoRedirect Pobiera lub ustawia, czy HttpClient wystąpienia powinny automatycznie śledzić odpowiedzi przekierowania. true
BaseAddress Pobiera lub ustawia podstawowy adres HttpClient wystąpień. http://localhost
HandleCookies Pobiera lub ustawia, czy HttpClient wystąpienia powinny obsługiwać cookies. true
MaxAutomaticRedirections Pobiera lub ustawia maksymalną liczbę odpowiedzi przekierowania, które HttpClient powinny być przestrzegane przez wystąpienia. 7

Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do CreateClient() metody (wartości domyślne są wyświetlane w przykładzie kodu):

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

Wstrzykiwanie makiety usług

Usługi można przesłonić w teście za pomocą wywołania metody w ConfigureTestServices konstruktorze hosta. Aby wstrzyknąć pozorne usługi, sut musi mieć klasę StartupStartup.ConfigureServices z metodą .

Przykładowy kod SUT zawiera usługę o określonym zakresie, która zwraca cudzysłów. Cudzysłów jest osadzony w ukrytym polu na stronie Indeks po zażądaniu strony Indeks.

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

Po uruchomieniu aplikacji SUT zostanie wygenerowana następująca adiustacja:

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

Aby przetestować usługę i zacytować iniekcję w teście integracji, test wprowadza pozorną usługę do sut. Pozorna usługa zastępuje aplikację QuoteService usługą dostarczaną przez aplikację testową o nazwie 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 jest wywoływana, a usługa o określonym zakresie jest zarejestrowana:

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

Adiustacja utworzona podczas wykonywania testu odzwierciedla tekst cudzysłowu dostarczony przez TestQuoteServiceelement , w związku z czym potwierdzenie przechodzi:

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

Pozorowanie uwierzytelniania

Testy w AuthTests klasie sprawdzają, czy bezpieczny punkt końcowy:

  • Przekierowuje nieuwierzytelnionego użytkownika na stronę logowania aplikacji.
  • Zwraca zawartość dla uwierzytelnionego użytkownika.

W kodzie SUT /SecurePage strona używa AuthorizePage konwencji, aby zastosować element AuthorizeFilter do strony. Aby uzyskać więcej informacji, zobacz Razor Konwencje autoryzacji stron.

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

W teście Get_SecurePageRedirectsAnUnauthenticatedUser parametr jest ustawiony tak, WebApplicationFactoryClientOptions aby nie zezwalał na przekierowania, ustawiając wartość AllowAutoRedirect na false:

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

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

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

Zezwalając klientowi na obserwowanie przekierowania, można wykonać następujące kontrole:

  • Kod stanu zwrócony przez sut można sprawdzić pod kątem oczekiwanego HttpStatusCode.Redirect wyniku, a nie końcowy kod stanu po przekierowaniu na stronę logowania, która byłaby HttpStatusCode.OK.
  • Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana, aby potwierdzić, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie końcowej odpowiedzi strony logowania, gdzie Location nagłówek nie będzie obecny.

Aplikacja testowa może wyśmiewać element AuthenticationHandler<TOptions> w ConfigureTestServices celu przetestowania aspektów uwierzytelniania i autoryzacji. Minimalny scenariusz zwraca wartość 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);
    }
}

Element TestAuthHandler jest wywoływany w celu uwierzytelnienia użytkownika, gdy schemat uwierzytelniania jest ustawiony na Test miejsce, w którym AddAuthentication jest zarejestrowany dla elementu ConfigureTestServices. Ważne jest, Test aby schemat był zgodny ze schematem oczekiwanym przez aplikację. W przeciwnym razie uwierzytelnianie nie będzie działać.

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

Aby uzyskać więcej informacji na WebApplicationFactoryClientOptionstemat programu , zobacz sekcję Opcje klienta .

Ustawianie środowiska

Domyślnie środowisko hosta i aplikacji sut jest skonfigurowane do korzystania ze środowiska deweloperskiego. Aby zastąpić środowisko sut podczas korzystania z polecenia IHostBuilder:

  • Ustaw zmienną ASPNETCORE_ENVIRONMENT środowiskową (na przykład , ProductionStaginglub inną wartość niestandardową, taką jak Testing).
  • Zastąpij CreateHostBuilder w aplikacji testowej, aby odczytywać zmienne środowiskowe poprzedzone prefiksem ASPNETCORE.
protected override IHostBuilder CreateHostBuilder() =>
    base.CreateHostBuilder()
        .ConfigureHostConfiguration(
            config => config.AddEnvironmentVariables("ASPNETCORE"));

Jeśli sut używa hosta sieci Web (IWebHostBuilder), przesłoń CreateWebHostBuilder:

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

Jak infrastruktura testowa wnioskuje ścieżkę główną zawartości aplikacji

Konstruktor WebApplicationFactory wywnioskuje ścieżkę katalogu głównego zawartości aplikacji, wyszukując element WebApplicationFactoryContentRootAttribute w zestawie zawierającym testy integracji z kluczem równym zestawowi System.Reflection.Assembly.FullNameTEntryPoint . Jeśli atrybut z poprawnym kluczem nie zostanie znaleziony, WebApplicationFactory wróć do wyszukiwania pliku rozwiązania (sln) i dołącza TEntryPoint nazwę zestawu do katalogu rozwiązania. Katalog główny aplikacji (ścieżka główna zawartości) służy do odnajdywania widoków i plików zawartości.

Wyłączanie kopiowania w tle

Kopiowanie w tle powoduje wykonanie testów w innym katalogu niż katalog wyjściowy. Jeśli testy polegają na ładowaniu plików względem Assembly.Location i napotykasz problemy, może być konieczne wyłączenie kopiowania w tle.

Aby wyłączyć kopiowanie w tle podczas korzystania z narzędzia xUnit, utwórz xunit.runner.json plik w katalogu projektu testowego z poprawnym ustawieniem konfiguracji:

{
  "shadowCopy": false
}

Usuwanie obiektów

Po wykonaniu IClassFixtureTestServer testów implementacji i HttpClient usunięciu elementu xUnit z klasy WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, należy je usunąć w implementacji IClassFixture . Aby uzyskać więcej informacji, zobacz Implementowanie metody Dispose.

Przykład testów integracji

Przykładowa aplikacja składa się z dwóch aplikacji:

Aplikacja Katalog projektu Opis
Aplikacja komunikatów (SUT) src/RazorPagesProject Umożliwia użytkownikowi dodawanie, usuwanie jednego, usuwanie wszystkich i analizowanie komunikatów.
Testowanie aplikacji tests/RazorPagesProject.Tests Służy do testowania integracji sut.

Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio. Jeśli używasz Visual Studio Code lub wiersza polecenia, wykonaj następujące polecenie w wierszu polecenia w tests/RazorPagesProject.Tests katalogu:

dotnet test

Organizacja aplikacji komunikatów (SUT)

Sut to Razor system komunikatów stron o następujących cechach:

  • Strona Indeks aplikacji (Pages/Index.cshtml i Pages/Index.cshtml.cs) zawiera interfejs użytkownika i metody modelu strony do kontrolowania dodawania, usuwania i analizy komunikatów (średnie wyrazy na komunikat).
  • Komunikat jest opisywany przez klasę Message (Data/Message.cs) z dwiema właściwościami: Id (klucz) i Text (message). Właściwość jest wymagana i ograniczona Text do 200 znaków.
  • Komunikaty są przechowywane przy użyciu bazy danych w pamięci programu Entity Framework†.
  • Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie AppDbContext kontekstu bazy danych (Data/AppDbContext.cs).
  • Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów jest inicjowany z trzema komunikatami.
  • Aplikacja zawiera element, do którego /SecurePage można uzyskać dostęp tylko przez uwierzytelnionego użytkownika.

†W temacie EF Test with InMemory (Testowanie przy użyciu programu InMemory) wyjaśniono, jak używać bazy danych w pamięci do testów przy użyciu narzędzia MSTest. W tym temacie jest używana struktura testów xUnit . Koncepcje testów i implementacje testów w różnych strukturach testów są podobne, ale nie identyczne.

Mimo że aplikacja nie używa wzorca repozytorium i nie jest skutecznym przykładem wzorca Unit of Work (UoW), Razor strony obsługują te wzorce programowania. Aby uzyskać więcej informacji, zobacz Projektowanie warstwy trwałości infrastruktury i Logika kontrolera testów (przykład implementuje wzorzec repozytorium).

Testowanie organizacji aplikacji

Aplikacja testowa jest aplikacją konsolową w tests/RazorPagesProject.Tests katalogu .

Testowanie katalogu aplikacji Opis
AuthTests Zawiera metody testowe dla:
  • Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelnionego użytkownika.
  • Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika za pomocą pozornego AuthenticationHandler<TOptions>elementu .
  • Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie identyfikatora logowania użytkownika profilu.
BasicTests Zawiera metodę testową routingu i typu zawartości.
IntegrationTests Zawiera testy integracji dla strony Indeks przy użyciu klasy niestandardowej WebApplicationFactory .
Helpers/Utilities
  • Utilities.cs Zawiera metodę InitializeDbForTests używaną do rozmieszczania bazy danych przy użyciu danych testowych.
  • HtmlHelpers.cs Metoda zapewnia metodę zwracania elementu AngleSharp IHtmlDocument do użycia przez metody testowe.
  • HttpClientExtensions.cs zapewnić przeciążenia do SendAsync przesyłania żądań do sut.

Struktura testowa to xUnit. Testy integracji są przeprowadzane przy użyciu elementu Microsoft.AspNetCore.TestHost, który zawiera element TestServer. Microsoft.AspNetCore.Mvc.Testing Ponieważ pakiet jest używany do konfigurowania hosta testowego i serwera testowego, TestHost pakiety i TestServer nie wymagają odwołań do pakietów bezpośrednich w pliku projektu aplikacji testowej ani konfiguracji dewelopera w aplikacji testowej.

Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu. Na przykład test usuwania wywołuje usunięcie rekordu bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia powiodło się.

Przykładowa aplikacja zawiera bazę danych z trzema komunikatami w Utilities.cs tym te testy mogą być używane podczas wykonywania:

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

Kontekst bazy danych SUT jest zarejestrowany w jego Startup.ConfigureServices metodzie. Wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane po wykonaniu Startup.ConfigureServices kodu aplikacji. Aby użyć innej bazy danych do testów, kontekst bazy danych aplikacji musi zostać zastąpiony w pliku builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Dostosowywanie elementu WebApplicationFactory .

W przypadku jednostek UWIERZYTELNIANIA, które nadal korzystają z hosta sieci Web, wywołanie zwrotne aplikacji builder.ConfigureServices testowej jest wykonywane przed kodem SUT Startup.ConfigureServices . Wywołanie zwrotne aplikacji testowej builder.ConfigureTestServices jest wykonywane po.

Dodatkowe zasoby