Testy integracji w ASP.NET CoreIntegration tests in ASP.NET Core

Luke Latham, Javier Calvarro Nelson, Steve Kowalskii Jos van derBy Luke Latham, Javier Calvarro Nelson, Steve Smith, and Jos van der Til

Testy integracji zapewniają, że składniki aplikacji działają prawidłowo na poziomie, który obejmuje infrastrukturę pomocniczą aplikacji, taką jak baza danych, system plików i sieć.Integration tests ensure that an app's components function correctly at a level that includes the app's supporting infrastructure, such as the database, file system, and network. ASP.NET Core obsługuje testy integracji przy użyciu struktury testów jednostkowych z testowym hostem sieci Web i serwerem testowym w pamięci.ASP.NET Core supports integration tests using a unit test framework with a test web host and an in-memory test server.

W tym temacie założono podstawową wiedzę na temat testów jednostkowych.This topic assumes a basic understanding of unit tests. Jeśli nie znasz pojęć testowych, zobacz testy jednostkowe w programie .NET Core i w .NET Standard tematu oraz jego połączonej zawartości.If unfamiliar with test concepts, see the Unit Testing in .NET Core and .NET Standard topic and its linked content.

Wyświetl lub pobierz przykładowy kod (jak pobrać)View or download sample code (how to download)

Przykładowa aplikacja jest aplikacją Razor Pages i przyjmuje podstawową wiedzę na temat Razor Pages.The sample app is a Razor Pages app and assumes a basic understanding of Razor Pages. Jeśli nie znasz Razor Pages, zobacz następujące tematy:If unfamiliar with Razor Pages, see the following topics:

Uwaga

W przypadku testowania aplikacji jednostronicowych zaleca się używanie narzędzia, takiego jak selen, które umożliwia automatyzację przeglądarki.For testing SPAs, we recommended a tool such as Selenium, which can automate a browser.

Wprowadzenie do testów integracjiIntroduction to integration tests

Testy integracji ocenią składniki aplikacji na szerszym poziomie niż testy jednostkowe.Integration tests evaluate an app's components on a broader level than unit tests. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy.Unit tests are used to test isolated software components, such as individual class methods. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą w celu uzyskania oczekiwanego wyniku, co może uwzględniać każdy składnik wymagany do pełnego przetworzenia żądania.Integration tests confirm that two or more app components work together to produce an expected result, possibly including every component required to fully process a request.

Te szersze testy są używane do testowania infrastruktury aplikacji i całego środowiska, często łącznie z następującymi składnikami:These broader tests are used to test the app's infrastructure and whole framework, often including the following components:

  • Baza danych programuDatabase
  • System plikówFile system
  • Urządzenia siecioweNetwork appliances
  • Potok żądania-odpowiedźRequest-response pipeline

Testy jednostkowe wykorzystują składniki, znane jako elementy sztuczne lub makiety, zamiast składników infrastruktury.Unit tests use fabricated components, known as fakes or mock objects, in place of infrastructure components.

W przeciwieństwie do testów jednostkowych, testy integracji:In contrast to unit tests, integration tests:

  • Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.Use the actual components that the app uses in production.
  • Wymagaj większej ilości kodu i przetwarzania danych.Require more code and data processing.
  • Trwa dłużej.Take longer to run.

W związku z tym Ogranicz korzystanie z testów integracji do najważniejszych scenariuszy infrastruktury.Therefore, limit the use of integration tests to the most important infrastructure scenarios. Jeśli można przetestować zachowanie przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.If a behavior can be tested using either a unit test or an integration test, choose the unit test.

Porada

Nie zapisuj testów integracji dla każdej możliwej permutacji danych i dostępu do plików z bazami danych i systemami plików.Don't write integration tests for every possible permutation of data and file access with databases and file systems. Bez względu na to, ile miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów do odczytu, zapisu, aktualizacji i usuwania umożliwia zwykle testowanie składników bazy danych i systemu plików.Regardless of how many places across an app interact with databases and file systems, a focused set of read, write, update, and delete integration tests are usually capable of adequately testing database and file system components. Użyj testów jednostkowych do rutynowych testów logiki metod, które współpracują z tymi składnikami.Use unit tests for routine tests of method logic that interact with these components. W testach jednostkowych użycie sztucznych/imitacji infrastruktury powoduje szybsze wykonywanie testów.In unit tests, the use of infrastructure fakes/mocks result in faster test execution.

Uwaga

W dyskusjach dotyczących testów integracji testowany projekt jest często określany jako testowany systemlub "SUT".In discussions of integration tests, the tested project is frequently called the System Under Test, or "SUT" for short.

"SUT" jest używany w tym temacie w celu odwoływania się do testowanej aplikacji ASP.NET Core."SUT" is used throughout this topic to refer to the tested ASP.NET Core app.

ASP.NET Core testy integracjiASP.NET Core integration tests

Testy integracji w ASP.NET Core wymagają następujących czynności:Integration tests in ASP.NET Core require the following:

  • Projekt testowy służy do znajdowania i wykonywania testów.A test project is used to contain and execute the tests. Projekt testowy ma odwołanie do SUT.The test project has a reference to the SUT.
  • Projekt testowy tworzy testowy host sieci Web dla SUT i używa klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.The test project creates a test web host for the SUT and uses a test server client to handle requests and responses with the SUT.
  • Moduł uruchamiający testy służy do wykonywania testów i raportujących wyniki testów.A test runner is used to execute the tests and report the test results.

Testy integracji są zgodne z sekwencją zdarzeń, które obejmują typowe kroki testu rozmieszczenia, działaniai potwierdzeń :Integration tests follow a sequence of events that include the usual Arrange, Act, and Assert test steps:

  1. SUT hosta sieci Web.The SUT's web host is configured.
  2. Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.A test server client is created to submit requests to the app.
  3. Krok rozmieszczania testu jest wykonywany: aplikacja testowa przygotowuje żądanie.The Arrange test step is executed: The test app prepares a request.
  4. Krok testu Act jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.The Act test step is executed: The client submits the request and receives the response.
  5. Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest sprawdzana jako przebieg lub Niepowodzenie w zależności od oczekiwanej odpowiedzi.The Assert test step is executed: The actual response is validated as a pass or fail based on an expected response.
  6. Proces jest kontynuowany, dopóki wszystkie testy nie zostaną wykonane.The process continues until all of the tests are executed.
  7. Wyniki testu są zgłaszane.The test results are reported.

Test hosta sieci Web jest zwykle skonfigurowany inaczej niż normalny host sieci Web aplikacji dla przebiegów testowych.Usually, the test web host is configured differently than the app's normal web host for the test runs. Na przykład dla testów można użyć innej bazy danych lub różnych ustawień aplikacji.For example, a different database or different app settings might be used for the tests.

Składniki infrastruktury, takie jak test hosta sieci Web i serwer testu w pamięci (TestServer), są udostępniane lub zarządzane przez pakiet Microsoft. AspNetCore. MVC. test .Infrastructure components, such as the test web host and in-memory test server (TestServer), are provided or managed by the Microsoft.AspNetCore.Mvc.Testing package. Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.Use of this package streamlines test creation and execution.

Pakiet Microsoft.AspNetCore.Mvc.Testing obsługuje następujące zadania:The Microsoft.AspNetCore.Mvc.Testing package handles the following tasks:

  • Kopiuje plik zależności ( . deps) z SUT do katalogu bin projektu testowego.Copies the dependencies file (.deps) from the SUT into the test project's bin directory.
  • Ustawia katalog główny zawartości dla katalogu głównego projektu SUT, aby umożliwić znalezienie plików statycznych i stron/widoków podczas wykonywania testów.Sets the content root to the SUT's project root so that static files and pages/views are found when the tests are executed.
  • Udostępnia klasę WebApplicationFactory do usprawnienia uruchamiania SUT przy użyciu TestServer.Provides the WebApplicationFactory class to streamline bootstrapping the SUT with TestServer.

Dokumentacja testów jednostkowych opisuje sposób konfigurowania projektu testowego i modułu testowego, a także szczegółowe instrukcje dotyczące sposobu uruchamiania testów i zaleceń dotyczących sposobu określania nazw testów i klas testowych.The unit tests documentation describes how to set up a test project and test runner, along with detailed instructions on how to run tests and recommendations for how to name tests and test classes.

Uwaga

Podczas tworzenia projektu testowego dla aplikacji należy oddzielić testy jednostkowe od testów integracji do różnych projektów.When creating a test project for an app, separate the unit tests from the integration tests into different projects. Dzięki temu składniki do testowania infrastruktury nie są przypadkowo uwzględniane w testach jednostkowych.This helps ensure that infrastructure testing components aren't accidentally included in the unit tests. Rozdzielenie testów jednostkowych i integracyjnych umożliwia również kontrolę nad tym, który zestaw testów jest uruchamiany.Separation of unit and integration tests also allows control over which set of tests are run.

Nie istnieje praktycznie żadna różnica między konfiguracją testów aplikacji Razor Pages i aplikacji MVC.There's virtually no difference between the configuration for tests of Razor Pages apps and MVC apps. Jedyną różnicą jest to, jak te testy są nazywane.The only difference is in how the tests are named. W aplikacji Razor Pages testy punktów końcowych stron są zwykle nazywane po klasie modelu strony (na przykład IndexPageTests do testowania integracji składników na stronie indeks).In a Razor Pages app, tests of page endpoints are usually named after the page model class (for example, IndexPageTests to test component integration for the Index page). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolera i nazwane po testowaniu kontrolerów (na przykład HomeControllerTests do integracji składnika testowego dla kontrolera macierzystego).In an MVC app, tests are usually organized by controller classes and named after the controllers they test (for example, HomeControllerTests to test component integration for the Home controller).

Wymagania wstępne aplikacji testowejTest app prerequisites

Projekt testowy musi:The test project must:

Te wymagania wstępne można zobaczyć w przykładowej aplikacji.These prerequisites can be seen in the sample app. Sprawdź plik Tests/RazorPagesProject. Tests/RazorPagesProject. tests. csproj .Inspect the tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj file. Przykładowa aplikacja używa środowiska testowego xUnit i biblioteki analizatora AngleSharp , dzięki czemu Przykładowa aplikacja również odwołuje się do:The sample app uses the xUnit test framework and the AngleSharp parser library, so the sample app also references:

Entity Framework Core jest również używany w testach.Entity Framework Core is also used in the tests. Odwołania do aplikacji:The app references:

Środowisko SUTSUT environment

Jeśli środowisko SUT nie jest ustawione, środowisko jest domyślnie stosowane do programowania.If the SUT's environment isn't set, the environment defaults to Development.

Podstawowe testy z domyślną WebApplicationFactoryBasic tests with the default WebApplicationFactory

WebApplicationFactory<TEntryPoint > służy do tworzenia TestServer dla testów integracji.WebApplicationFactory<TEntryPoint> is used to create a TestServer for the integration tests. TEntryPoint jest klasą punktu wejścia SUT, zazwyczaj klasy Startup.TEntryPoint is the entry point class of the SUT, usually the Startup class.

Klasy testowe implementują interfejs armatury klasy (IClassFixture) w celu wskazania, że Klasa zawiera testy i udostępnia wystąpienia obiektów udostępnionych w ramach testów w klasie.Test classes implement a class fixture interface (IClassFixture) to indicate the class contains tests and provide shared object instances across the tests in the class.

Następująca Klasa testowa, BasicTests, używa WebApplicationFactory do ładowania SUT i zapewnienia HttpClient do metody testowej, Get_EndpointsReturnSuccessAndCorrectContentType.The following test class, BasicTests, uses the WebApplicationFactory to bootstrap the SUT and provide an HttpClient to a test method, Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi powiedzie się (kody stanu z zakresu 200-299), a nagłówek Content-Type jest text/html; charset=utf-8 dla kilku stron aplikacji.The method checks if the response status code is successful (status codes in the range 200-299) and the Content-Type header is text/html; charset=utf-8 for several app pages.

Klient tworzy wystąpienie HttpClient, które automatycznie następuje przekierowanie i obsługę plików cookie.CreateClient creates an instance of HttpClient that automatically follows redirects and handles 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 nieistotne pliki cookie nie są zachowywane między żądaniami, gdy zasady zgody Rodo są włączone.By default, non-essential cookies aren't preserved across requests when the GDPR consent policy is enabled. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach.To preserve non-essential cookies, such as those used by the TempData provider, mark them as essential in your tests. Aby uzyskać instrukcje dotyczące oznaczania pliku cookie jako kluczowego, zobacz podstawowe pliki cookie.For instructions on marking a cookie as essential, see Essential cookies.

Dostosuj WebApplicationFactoryCustomize WebApplicationFactory

Konfigurację hosta sieci Web można utworzyć niezależnie od klas testów przez dziedziczenie z WebApplicationFactory w celu utworzenia jednego lub kilku fabryk niestandardowych:Web host configuration can be created independently of the test classes by inheriting from WebApplicationFactory to create one or more custom factories:

  1. Dziedzicz po WebApplicationFactory i Zastąp ConfigureWebHost.Inherit from WebApplicationFactory and override ConfigureWebHost. IWebHostBuilder umożliwia konfigurację kolekcji usług z ConfigureServices:The IWebHostBuilder allows the configuration of the service collection with ConfigureServices:

    public class CustomWebApplicationFactory<TStartup>
        : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                // Remove the app's ApplicationDbContext registration.
                var descriptor = services.SingleOrDefault(
                    d => d.ServiceType ==
                        typeof(DbContextOptions<ApplicationDbContext>));
    
                if (descriptor != null)
                {
                    services.Remove(descriptor);
                }
    
                // Add ApplicationDbContext using an in-memory database for testing.
                services.AddDbContext<ApplicationDbContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                });
    
                // Build the service provider.
                var sp = services.BuildServiceProvider();
    
                // Create a scope to obtain a reference to the database
                // context (ApplicationDbContext).
                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<ApplicationDbContext>();
                    var logger = scopedServices
                        .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
    
                    // Ensure the database is created.
                    db.Database.EnsureCreated();
    
                    try
                    {
                        // Seed the database with test data.
                        Utilities.InitializeDbForTests(db);
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, "An error occurred seeding the " +
                            "database with test messages. Error: {Message}", ex.Message);
                    }
                }
            });
        }
    }
    

    Wypełnianie bazy danych w przykładowej aplikacji jest wykonywane przez metodę InitializeDbForTests.Database seeding in the sample app is performed by the InitializeDbForTests method. Metoda jest opisana w przykładowej testów integracji: sekcja testowa aplikacja w organizacji .The method is described in the Integration tests sample: Test app organization section.

    Kontekst bazy danych SUT jest zarejestrowany w Startup.ConfigureServices metodzie.The SUT's database context is registered in its Startup.ConfigureServices method. Wywołanie zwrotne builder.ConfigureServices aplikacji testowej jest wykonywane po wykonaniu kodu Startup.ConfigureServices aplikacji.The test app's builder.ConfigureServices callback is executed after the app's Startup.ConfigureServices code is executed. Kolejność wykonywania jest istotną zmianą dla hosta ogólnego w wersji ASP.NET Core 3,0.The execution order is a breaking change for the Generic Host with the release of ASP.NET Core 3.0. Aby użyć innej bazy danych dla testów niż baza danych aplikacji, należy zamienić kontekst bazy danych aplikacji na builder.ConfigureServices.To use a different database for the tests than the app's database, the app's database context must be replaced in builder.ConfigureServices.

    Przykładowa aplikacja znajduje deskryptor usługi dla kontekstu bazy danych i używa deskryptora do usunięcia rejestracji usługi.The sample app finds the service descriptor for the database context and uses the descriptor to remove the service registration. Następnie fabryka dodaje nową ApplicationDbContext, która korzysta z bazy danych w pamięci dla testów.Next, the factory adds a new ApplicationDbContext that uses an in-memory database for the tests.

    Aby nawiązać połączenie z inną bazą danych niż baza danych w pamięci, Zmień wywołanie UseInMemoryDatabase, aby połączyć kontekst z inną bazą danych.To connect to a different database than the in-memory database, change the UseInMemoryDatabase call to connect the context to a different database. Aby użyć SQL Server testowej bazy danych:To use a SQL Server test database:

    services.AddDbContext<ApplicationDbContext>((options, context) => 
    {
        context.UseSqlServer(
            Configuration.GetConnectionString("TestingDbConnectionString"));
    });
    
  2. Użyj niestandardowych CustomWebApplicationFactory w klasach testowych.Use the custom CustomWebApplicationFactory in test classes. Poniższy przykład używa fabryki w klasie IndexPageTests:The following example uses the factory in the IndexPageTests class:

    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 aplikacji przykładowej jest skonfigurowany tak, aby uniemożliwiać HttpClient z następujących przekierowań.The sample app's client is configured to prevent the HttpClient from following redirects. Jak wyjaśniono w dalszej części sekcji uwierzytelnianie , pozwala to testom sprawdzić wynik pierwszej odpowiedzi aplikacji.As explained later in the Mock authentication section, this permits tests to check the result of the app's first response. Pierwsza odpowiedź to przekierowanie w wielu z tych testów z nagłówkiem Location.The first response is a redirect in many of these tests with a Location header.

  3. Typowy test używa HttpClient i metod pomocniczych do przetwarzania żądania i odpowiedzi:A typical test uses the HttpClient and helper methods to process the request and the response:

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

Wszelkie żądania POST do SUT muszą być zgodne z sprawdzeniem, czy jest ono automatycznie wykonywane przez system ochrony danych przed fałszerstwem.Any POST request to the SUT must satisfy the antiforgery check that's automatically made by the app's data protection antiforgery system. Aby można było rozmieścić żądanie POST testu, aplikacja testowa musi:In order to arrange for a test's POST request, the test app must:

  1. Utwórz żądanie dla strony.Make a request for the page.
  2. Przeanalizuj plik cookie dotyczący fałszowania i token walidacji żądania z odpowiedzi.Parse the antiforgery cookie and request validation token from the response.
  3. Wprowadź żądanie POST przy użyciu pliku cookie służącego do fałszerstwa i tokenu walidacji żądania.Make the POST request with the antiforgery cookie and request validation token in place.

Metody rozszerzenia pomocnika SendAsync (pomocnicys/HttpClientExtensions. cs) oraz metoda pomocnika GetDocumentAsync (pomocnicys/HtmlHelpers. cs) w przykładowej aplikacji używają analizatora AngleSharp do obsługi kontroli przed fałszerstwem przy użyciu następujących metod:The SendAsync helper extension methods (Helpers/HttpClientExtensions.cs) and the GetDocumentAsync helper method (Helpers/HtmlHelpers.cs) in the sample app use the AngleSharp parser to handle the antiforgery check with the following methods:

  • GetDocumentAsync – odbiera HttpResponseMessage i zwraca IHtmlDocument.GetDocumentAsync – Receives the HttpResponseMessage and returns an IHtmlDocument. GetDocumentAsync używa fabryki przygotowującej odpowiedź wirtualną na podstawie oryginalnego HttpResponseMessage.GetDocumentAsync uses a factory that prepares a virtual response based on the original HttpResponseMessage. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją AngleSharp.For more information, see the AngleSharp documentation.
  • SendAsync metody rozszerzające HttpClient tworzenia HttpRequestMessage i wywołania SendAsync (HttpRequestMessage) w celu przesyłania żądań do SUT.SendAsync extension methods for the HttpClient compose an HttpRequestMessage and call SendAsync(HttpRequestMessage) to submit requests to the SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:Overloads for SendAsync accept the HTML form (IHtmlFormElement) and the following:
    • Przycisk przesyłania formularza (IHtmlElement)Submit button of the form (IHtmlElement)
    • Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)Form values collection (IEnumerable<KeyValuePair<string, string>>)
    • Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)Submit button (IHtmlElement) and form values (IEnumerable<KeyValuePair<string, string>>)

Uwaga

AngleSharp to biblioteka analizy innej firmy używana do celów demonstracyjnych w tym temacie i Przykładowa aplikacja.AngleSharp is a third-party parsing library used for demonstration purposes in this topic and the sample app. AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core.AngleSharp isn't supported or required for integration testing of ASP.NET Core apps. Można użyć innych analizatorów, takich jak pakiet HAP (html).Other parsers can be used, such as the Html Agility Pack (HAP). Innym podejściem jest napisać kod, który będzie obsługiwał token weryfikacji żądań systemu i bezfałszowający plik cookie.Another approach is to write code to handle the antiforgery system's request verification token and antiforgery cookie directly.

Dostosowywanie klienta przy użyciu WithWebHostBuilderCustomize the client with WithWebHostBuilder

Gdy w metodzie testowej jest wymagana dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory z IWebHostBuilder , który jest bardziej dostosowany przez konfigurację.When additional configuration is required within a test method, WithWebHostBuilder creates a new WebApplicationFactory with an IWebHostBuilder that is further customized by configuration.

Metoda testowania Post_DeleteMessageHandler_ReturnsRedirectToRoot przykładowej aplikacji ilustruje użycie WithWebHostBuilder.The Post_DeleteMessageHandler_ReturnsRedirectToRoot test method of the sample app demonstrates the use of WithWebHostBuilder. Ten test służy do usuwania rekordów w bazie danych przez wyzwolenie przesłania formularza w SUT.This test performs a record delete in the database by triggering a form submission in the SUT.

Ponieważ inny test w klasie IndexPageTests wykonuje operację, która usuwa wszystkie rekordy z bazy danych i może być uruchomiona przed metodą Post_DeleteMessageHandler_ReturnsRedirectToRoot, baza danych zostanie oddzielna w tej metodzie testowej, aby upewnić się, że rekord jest obecny dla SUT do usunięcia.Because another test in the IndexPageTests class performs an operation that deletes all of the records in the database and may run before the Post_DeleteMessageHandler_ReturnsRedirectToRoot method, the database is reseeded in this test method to ensure that a record is present for the SUT to delete. Wybranie pierwszego przycisku Usuń formularza messages w SUT jest symulowane w żądaniu do SUT:Selecting the first delete button of the messages form in the SUT is simulated in the request to the 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 klientaClient options

W poniższej tabeli przedstawiono domyślne WebApplicationFactoryClientOptions dostępne podczas tworzenia wystąpień HttpClient.The following table shows the default WebApplicationFactoryClientOptions available when creating HttpClient instances.

OpcjaOption OpisDescription DomyślnyDefault
AllowAutoRedirectAllowAutoRedirect Pobiera lub ustawia czy HttpClient wystąpienia powinny automatycznie śledzić odpowiedzi przekierowania.Gets or sets whether or not HttpClient instances should automatically follow redirect responses. true
BaseAddressBaseAddress Pobiera lub ustawia adres podstawowy HttpClient wystąpień.Gets or sets the base address of HttpClient instances. http://localhost
HandleCookiesHandleCookies Pobiera lub ustawia czy HttpClient wystąpienia powinny obsługiwać pliki cookie.Gets or sets whether HttpClient instances should handle cookies. true
MaxAutomaticRedirectionsMaxAutomaticRedirections Pobiera lub ustawia maksymalną liczbę odpowiedzi przekierowań, które powinny następować HttpClient wystąpieniami.Gets or sets the maximum number of redirect responses that HttpClient instances should follow. 77

Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do metody onclient (wartości domyślne są pokazane w przykładzie kodu):Create the WebApplicationFactoryClientOptions class and pass it to the CreateClient method (default values are shown in the code example):

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

Wsuń usługi imitacjiInject mock services

Usługi można przesłaniać w teście, używając wywołania ConfigureTestServices na konstruktorze hosta.Services can be overridden in a test with a call to ConfigureTestServices on the host builder. Aby wstrzyknąć usługi makiety, SUT musi mieć klasę Startup z metodą Startup.ConfigureServices.To inject mock services, the SUT must have a Startup class with a Startup.ConfigureServices method.

Przykład SUT obejmuje usługę objętą zakresem zwracającą ofertę.The sample SUT includes a scoped service that returns a quote. Po zażądaniu strony indeksu oferta zostanie osadzona w ukrytym polu na stronie indeksu.The quote is embedded in a hidden field on the Index page when the Index page is requested.

Services/IQuoteService.cs:Services/IQuoteService.cs:

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

Usługi/QuoteService. cs: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:Startup.cs:

services.AddScoped<IQuoteService, QuoteService>();

Pages/index. cshtml. cs: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();
    }

Strony/indeks. cs:Pages/Index.cs:

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

Po uruchomieniu aplikacji SUT jest generowany następujący znacznik:The following markup is generated when the SUT app is run:

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

W celu przetestowania usługi i iniekcji cytatu w teście integracji, usługa makiety jest wstrzykiwana do SUT przez test.To test the service and quote injection in an integration test, a mock service is injected into the SUT by the test. Usługa makiety zastępuje QuoteService aplikacji za pomocą usługi dostarczonej przez aplikację testową o nazwie TestQuoteService:The mock service replaces the app's QuoteService with a service provided by the test app, called TestQuoteService:

IntegrationTests.IndexPageTests.cs: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:ConfigureTestServices is called, and the scoped service is registered:

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

Adiustacje powstające podczas wykonywania testu odzwierciedlają tekst cytatu dostarczony przez TestQuoteService, w związku z czym potwierdzenie przebiega:The markup produced during the test's execution reflects the quote text supplied by TestQuoteService, thus the assertion passes:

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

Uwierzytelnianie w postaci makietyMock authentication

Testy w klasie AuthTests sprawdzają, czy bezpieczny punkt końcowy:Tests in the AuthTests class check that a secure endpoint:

  • Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.Redirects an unauthenticated user to the app's Login page.
  • Zwraca zawartość dla uwierzytelnionego użytkownika.Returns content for an authenticated user.

W SUT Strona /SecurePage używa konwencji AuthorizePage w celu zastosowania AuthorizeFilter do strony.In the SUT, the /SecurePage page uses an AuthorizePage convention to apply an AuthorizeFilter to the page. Aby uzyskać więcej informacji, zobacz Razor Pages Konwencji autoryzacji.For more information, see Razor Pages authorization conventions.

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

W teście Get_SecurePageRedirectsAnUnauthenticatedUser WebApplicationFactoryClientOptions jest ustawiony tak, aby nie zezwalać na przekierowania przez ustawienie AllowAutoRedirect na false:In the Get_SecurePageRedirectsAnUnauthenticatedUser test, a WebApplicationFactoryClientOptions is set to disallow redirects by setting AllowAutoRedirect to 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);
}

W przypadku niezezwalania klientowi na śledzenie przekierowania można wykonać następujące operacje:By disallowing the client to follow the redirect, the following checks can be made:

  • Kod stanu zwracany przez SUT można sprawdzić pod kątem oczekiwanego wyniku HttpStatusCode. Redirect , a nie do końcowego kodu stanu po przekierowaniu do strony logowania, która byłaby HttpStatusCode. ok.The status code returned by the SUT can be checked against the expected HttpStatusCode.Redirect result, not the final status code after the redirect to the Login page, which would be HttpStatusCode.OK.
  • Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana w celu potwierdzenia, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od końcowej odpowiedzi na stronę logowania, gdzie nagłówek Location nie będzie obecny.The Location header value in the response headers is checked to confirm that it starts with http://localhost/Identity/Account/Login, not the final Login page response, where the Location header wouldn't be present.

Aplikacja testowa może zasymulować AuthenticationHandler<TOptions> w ConfigureTestServices , aby przetestować aspekty uwierzytelniania i autoryzacji.The test app can mock an AuthenticationHandler<TOptions> in ConfigureTestServices in order to test aspects of authentication and authorization. Minimalny scenariusz zwraca element AuthenticateResult. Success:A minimal scenario returns an 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);
    }
}

TestAuthHandler jest wywoływana w celu uwierzytelnienia użytkownika, gdy w schemacie uwierzytelniania jest ustawiona wartość Test, gdzie AddAuthentication jest zarejestrowana dla ConfigureTestServices:The TestAuthHandler is called to authenticate a user when the authentication scheme is set to Test where AddAuthentication is registered for ConfigureTestServices:

[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 temat WebApplicationFactoryClientOptions, zobacz sekcję Opcje klienta .For more information on WebApplicationFactoryClientOptions, see the Client options section.

Ustawianie środowiskaSet the environment

Domyślnie środowisko hosta i aplikacji SUT jest skonfigurowane do korzystania ze środowiska deweloperskiego.By default, the SUT's host and app environment is configured to use the Development environment. Aby zastąpić środowisko SUT:To override the SUT's environment:

  • Ustaw zmienną środowiskową ASPNETCORE_ENVIRONMENT (na przykład Staging, Productionlub inną wartość niestandardową, taką jak Testing).Set the ASPNETCORE_ENVIRONMENT environment variable (for example, Staging, Production, or other custom value, such as Testing).
  • Przesłoń CreateHostBuilder w aplikacji testowej, aby odczytać zmienne środowiskowe z prefiksem ASPNETCORE.Override CreateHostBuilder in the test app to read environment variables prefixed with ASPNETCORE.
protected override IHostBuilder CreateHostBuilder() => 
    base.CreateHostBuilder()
        .ConfigureHostConfiguration(
            config => config.AddEnvironmentVariables("ASPNETCORE"));

Jak infrastruktura testowa wnioskuje ścieżkę katalogu głównego zawartości aplikacjiHow the test infrastructure infers the app content root path

Konstruktor WebApplicationFactory wnioskuje ścieżkę katalogu głównego zawartości aplikacji, wyszukując WebApplicationFactoryContentRootAttribute na zestawie zawierającym testy integracji z kluczem równym zestawowi TEntryPoint System.Reflection.Assembly.FullName.The WebApplicationFactory constructor infers the app content root path by searching for a WebApplicationFactoryContentRootAttribute on the assembly containing the integration tests with a key equal to the TEntryPoint assembly System.Reflection.Assembly.FullName. W przypadku nieznalezienia atrybutu o poprawnym kluczu WebApplicationFactory powracanie do wyszukiwania pliku rozwiązania ( . sln) i dołącza nazwę zestawu TEntryPoint do katalogu rozwiązania.In case an attribute with the correct key isn't found, WebApplicationFactory falls back to searching for a solution file (.sln) and appends the TEntryPoint assembly name to the solution directory. Katalog główny aplikacji (ścieżka katalogu głównego zawartości) służy do odnajdywania widoków i plików zawartości.The app root directory (the content root path) is used to discover views and content files.

Wyłącz kopiowanie w tleDisable shadow copying

Kopiowanie w tle powoduje, że testy są wykonywane w innym katalogu niż katalog wyjściowy.Shadow copying causes the tests to execute in a different directory than the output directory. Aby testy działały prawidłowo, kopiowanie w tle musi być wyłączone.For tests to work properly, shadow copying must be disabled. Przykładowa aplikacja używa xUnit i wyłącza kopiowanie w tle dla xUnit, dołączając plik xUnit. Runner. JSON z prawidłowym ustawieniem konfiguracji.The sample app uses xUnit and disables shadow copying for xUnit by including an xunit.runner.json file with the correct configuration setting. Aby uzyskać więcej informacji, zobacz Configuring xUnit with JSON.For more information, see Configuring xUnit with JSON.

Dodaj plik xUnit. Runner. JSON do katalogu głównego projektu testowego z następującą zawartością:Add the xunit.runner.json file to root of the test project with the following content:

{
  "shadowCopy": false
}

Usuwanie obiektówDisposal of objects

Po wykonaniu testów implementacji IClassFixture TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory.After the tests of the IClassFixture implementation are executed, TestServer and HttpClient are disposed when xUnit disposes of the WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, usuń je z implementacji IClassFixture.If objects instantiated by the developer require disposal, dispose of them in the IClassFixture implementation. Aby uzyskać więcej informacji, zobacz implementowanie metody Dispose.For more information, see Implementing a Dispose method.

Przykład testów integracjiIntegration tests sample

Przykładowa aplikacja składa się z dwóch aplikacji:The sample app is composed of two apps:

AplikacjaApp Katalog projektuProject directory OpisDescription
Aplikacja wiadomości (SUT)Message app (the SUT) src/RazorPagesProjectsrc/RazorPagesProject Zezwala użytkownikowi na dodawanie, usuwanie, usuwanie wszystkich i analizowanie komunikatów.Allows a user to add, delete one, delete all, and analyze messages.
Aplikacja testowaTest app tests/RazorPagesProject.Teststests/RazorPagesProject.Tests Służy do integracji testu SUT.Used to integration test the SUT.

Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio.The tests can be run using the built-in test features of an IDE, such as Visual Studio. W przypadku używania Visual Studio Code lub wiersza polecenia wykonaj następujące polecenie w wierszu polecenia w katalogu Tests/RazorPagesProject. Tests :If using Visual Studio Code or the command line, execute the following command at a command prompt in the tests/RazorPagesProject.Tests directory:

dotnet test

Organizacja aplikacji wiadomości (SUT)Message app (SUT) organization

SUT to system komunikatów Razor Pages o następujących cechach:The SUT is a Razor Pages message system with the following characteristics:

  • Strona indeks aplikacji (Pages/index. cshtml i Pages/index. cshtml. cs) zawiera metody interfejsu użytkownika i modelu strony umożliwiające sterowanie dodawaniem, usuwaniem i analizą komunikatów (średnia liczba wyrazów na komunikat).The Index page of the app (Pages/Index.cshtml and Pages/Index.cshtml.cs) provides a UI and page model methods to control the addition, deletion, and analysis of messages (average words per message).
  • Komunikat jest opisywany przez klasę Message (Data/Message. cs) z dwiema właściwościami: Id (Key) i Text (Message).A message is described by the Message class (Data/Message.cs) with two properties: Id (key) and Text (message). Właściwość Text jest wymagana i jest ograniczona do 200 znaków.The Text property is required and limited to 200 characters.
  • Komunikaty są przechowywane przy użyciu† bazy danych znajdującej się w pamięci Entity Framework.Messages are stored using Entity Framework's in-memory database†.
  • Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie kontekstu bazy danych AppDbContext (Data/AppDbContext. cs).The app contains a data access layer (DAL) in its database context class, AppDbContext (Data/AppDbContext.cs).
  • Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów zostanie zainicjowany przy użyciu trzech komunikatów.If the database is empty on app startup, the message store is initialized with three messages.
  • Aplikacja zawiera /SecurePage dostępną tylko dla uwierzytelnionego użytkownika.The app includes a /SecurePage that can only be accessed by an authenticated user.

†W temacie EF test z niepamięcią, wyjaśniono, jak korzystać z bazy danych w pamięci dla testów z MSTest.†The EF topic, Test with InMemory, explains how to use an in-memory database for tests with MSTest. W tym temacie jest stosowane środowisko testowe xUnit .This topic uses the xUnit test framework. Koncepcje testowe i implementacje testów w różnych strukturach testów są podobne, ale nie są identyczne.Test concepts and test implementations across different test frameworks are similar but not identical.

Mimo że aplikacja nie używa wzorca repozytorium i nie jest skutecznym przykładem wzorca jednostki pracy, Razor Pages obsługuje te wzorce rozwoju.Although the app doesn't use the repository pattern and isn't an effective example of the Unit of Work (UoW) pattern, Razor Pages supports these patterns of development. Aby uzyskać więcej informacji, zobacz projektowanie warstwy trwałości infrastruktury i logiki kontrolera testów (przykład implementuje wzorzec repozytorium).For more information, see Designing the infrastructure persistence layer and Test controller logic (the sample implements the repository pattern).

Testuj organizację aplikacjiTest app organization

Aplikacja testowa to Aplikacja konsolowa w katalogu Tests/RazorPagesProject. Tests .The test app is a console app inside the tests/RazorPagesProject.Tests directory.

Testuj katalog aplikacjiTest app directory OpisDescription
AuthTestsAuthTests Zawiera metody testowe dla:Contains test methods for:
  • Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelniony użytkownik.Accessing a secure page by an unauthenticated user.
  • Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika przy użyciu AuthenticationHandler<TOptions>imitacji.Accessing a secure page by an authenticated user with a mock AuthenticationHandler<TOptions>.
  • Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika w profilu.Obtaining a GitHub user profile and checking the profile's user login.
BasicTestsBasicTests Zawiera metodę testową dla routingu i typu zawartości.Contains a test method for routing and content type.
IntegrationTestsIntegrationTests Zawiera testy integracji dla strony indeksu przy użyciu niestandardowej klasy WebApplicationFactory.Contains the integration tests for the Index page using custom WebApplicationFactory class.
Pomocnicy/narzędziaHelpers/Utilities
  • Utilities.cs zawiera metodę InitializeDbForTests używaną do wypełniania bazy danych danymi testowymi.Utilities.cs contains the InitializeDbForTests method used to seed the database with test data.
  • HtmlHelpers.cs zapewnia metodę zwracającą IHtmlDocument AngleSharp do użycia przez metody testowe.HtmlHelpers.cs provides a method to return an AngleSharp IHtmlDocument for use by the test methods.
  • HttpClientExtensions.cs zapewniają przeciążenia dla SendAsync do przesyłania żądań do SUT.HttpClientExtensions.cs provide overloads for SendAsync to submit requests to the SUT.

Platforma testowa jest xUnit.The test framework is xUnit. Testy integracji są przeprowadzane przy użyciu programu Microsoft. AspNetCore. TestHost, który obejmuje TestServer.Integration tests are conducted using the Microsoft.AspNetCore.TestHost, which includes the TestServer. Ponieważ pakiet Microsoft. AspNetCore. MVC. test służy do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań do pakietów w pliku projektu aplikacji testowej ani w konfiguracji dewelopera w aplikacji testowej.Because the Microsoft.AspNetCore.Mvc.Testing package is used to configure the test host and test server, the TestHost and TestServer packages don't require direct package references in the test app's project file or developer configuration in the test app.

Umieszczanie bazy danych do testowaniaSeeding the database for testing

Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu.Integration tests usually require a small dataset in the database prior to the test execution. Na przykład można usunąć wywołania testu do usuwania rekordów bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia zakończyło się pomyślnie.For example, a delete test calls for a database record deletion, so the database must have at least one record for the delete request to succeed.

Przykładowa aplikacja odziarnauje bazę danych z trzema komunikatami w Utilities.cs , że testy mogą być używane podczas wykonywania:The sample app seeds the database with three messages in Utilities.cs that tests can use when they execute:

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 Startup.ConfigureServices metodzie.The SUT's database context is registered in its Startup.ConfigureServices method. Wywołanie zwrotne builder.ConfigureServices aplikacji testowej jest wykonywane po wykonaniu kodu Startup.ConfigureServices aplikacji.The test app's builder.ConfigureServices callback is executed after the app's Startup.ConfigureServices code is executed. Aby użyć innej bazy danych dla testów, kontekst bazy danych aplikacji musi zostać zastąpiony w builder.ConfigureServices.To use a different database for the tests, the app's database context must be replaced in builder.ConfigureServices. Aby uzyskać więcej informacji, zobacz sekcję Dostosowywanie WebApplicationFactory .For more information, see the Customize WebApplicationFactory section.

Testy integracji zapewniają, że składniki aplikacji działają prawidłowo na poziomie, który obejmuje infrastrukturę pomocniczą aplikacji, taką jak baza danych, system plików i sieć.Integration tests ensure that an app's components function correctly at a level that includes the app's supporting infrastructure, such as the database, file system, and network. ASP.NET Core obsługuje testy integracji przy użyciu struktury testów jednostkowych z testowym hostem sieci Web i serwerem testowym w pamięci.ASP.NET Core supports integration tests using a unit test framework with a test web host and an in-memory test server.

W tym temacie założono podstawową wiedzę na temat testów jednostkowych.This topic assumes a basic understanding of unit tests. Jeśli nie znasz pojęć testowych, zobacz testy jednostkowe w programie .NET Core i w .NET Standard tematu oraz jego połączonej zawartości.If unfamiliar with test concepts, see the Unit Testing in .NET Core and .NET Standard topic and its linked content.

Wyświetl lub pobierz przykładowy kod (jak pobrać)View or download sample code (how to download)

Przykładowa aplikacja jest aplikacją Razor Pages i przyjmuje podstawową wiedzę na temat Razor Pages.The sample app is a Razor Pages app and assumes a basic understanding of Razor Pages. Jeśli nie znasz Razor Pages, zobacz następujące tematy:If unfamiliar with Razor Pages, see the following topics:

Uwaga

W przypadku testowania aplikacji jednostronicowych zaleca się używanie narzędzia, takiego jak selen, które umożliwia automatyzację przeglądarki.For testing SPAs, we recommended a tool such as Selenium, which can automate a browser.

Wprowadzenie do testów integracjiIntroduction to integration tests

Testy integracji ocenią składniki aplikacji na szerszym poziomie niż testy jednostkowe.Integration tests evaluate an app's components on a broader level than unit tests. Testy jednostkowe służą do testowania izolowanych składników oprogramowania, takich jak poszczególne metody klasy.Unit tests are used to test isolated software components, such as individual class methods. Testy integracji potwierdzają, że co najmniej dwa składniki aplikacji współpracują ze sobą w celu uzyskania oczekiwanego wyniku, co może uwzględniać każdy składnik wymagany do pełnego przetworzenia żądania.Integration tests confirm that two or more app components work together to produce an expected result, possibly including every component required to fully process a request.

Te szersze testy są używane do testowania infrastruktury aplikacji i całego środowiska, często łącznie z następującymi składnikami:These broader tests are used to test the app's infrastructure and whole framework, often including the following components:

  • Baza danych programuDatabase
  • System plikówFile system
  • Urządzenia siecioweNetwork appliances
  • Potok żądania-odpowiedźRequest-response pipeline

Testy jednostkowe wykorzystują składniki, znane jako elementy sztuczne lub makiety, zamiast składników infrastruktury.Unit tests use fabricated components, known as fakes or mock objects, in place of infrastructure components.

W przeciwieństwie do testów jednostkowych, testy integracji:In contrast to unit tests, integration tests:

  • Użyj rzeczywistych składników używanych przez aplikację w środowisku produkcyjnym.Use the actual components that the app uses in production.
  • Wymagaj większej ilości kodu i przetwarzania danych.Require more code and data processing.
  • Trwa dłużej.Take longer to run.

W związku z tym Ogranicz korzystanie z testów integracji do najważniejszych scenariuszy infrastruktury.Therefore, limit the use of integration tests to the most important infrastructure scenarios. Jeśli można przetestować zachowanie przy użyciu testu jednostkowego lub testu integracji, wybierz test jednostkowy.If a behavior can be tested using either a unit test or an integration test, choose the unit test.

Porada

Nie zapisuj testów integracji dla każdej możliwej permutacji danych i dostępu do plików z bazami danych i systemami plików.Don't write integration tests for every possible permutation of data and file access with databases and file systems. Bez względu na to, ile miejsc w aplikacji współdziała z bazami danych i systemami plików, skoncentrowany zestaw testów do odczytu, zapisu, aktualizacji i usuwania umożliwia zwykle testowanie składników bazy danych i systemu plików.Regardless of how many places across an app interact with databases and file systems, a focused set of read, write, update, and delete integration tests are usually capable of adequately testing database and file system components. Użyj testów jednostkowych do rutynowych testów logiki metod, które współpracują z tymi składnikami.Use unit tests for routine tests of method logic that interact with these components. W testach jednostkowych użycie sztucznych/imitacji infrastruktury powoduje szybsze wykonywanie testów.In unit tests, the use of infrastructure fakes/mocks result in faster test execution.

Uwaga

W dyskusjach dotyczących testów integracji testowany projekt jest często określany jako testowany systemlub "SUT".In discussions of integration tests, the tested project is frequently called the System Under Test, or "SUT" for short.

"SUT" jest używany w tym temacie w celu odwoływania się do testowanej aplikacji ASP.NET Core."SUT" is used throughout this topic to refer to the tested ASP.NET Core app.

ASP.NET Core testy integracjiASP.NET Core integration tests

Testy integracji w ASP.NET Core wymagają następujących czynności:Integration tests in ASP.NET Core require the following:

  • Projekt testowy służy do znajdowania i wykonywania testów.A test project is used to contain and execute the tests. Projekt testowy ma odwołanie do SUT.The test project has a reference to the SUT.
  • Projekt testowy tworzy testowy host sieci Web dla SUT i używa klienta serwera testowego do obsługi żądań i odpowiedzi z SUT.The test project creates a test web host for the SUT and uses a test server client to handle requests and responses with the SUT.
  • Moduł uruchamiający testy służy do wykonywania testów i raportujących wyniki testów.A test runner is used to execute the tests and report the test results.

Testy integracji są zgodne z sekwencją zdarzeń, które obejmują typowe kroki testu rozmieszczenia, działaniai potwierdzeń :Integration tests follow a sequence of events that include the usual Arrange, Act, and Assert test steps:

  1. SUT hosta sieci Web.The SUT's web host is configured.
  2. Klient serwera testowego jest tworzony w celu przesyłania żądań do aplikacji.A test server client is created to submit requests to the app.
  3. Krok rozmieszczania testu jest wykonywany: aplikacja testowa przygotowuje żądanie.The Arrange test step is executed: The test app prepares a request.
  4. Krok testu Act jest wykonywany: klient przesyła żądanie i odbiera odpowiedź.The Act test step is executed: The client submits the request and receives the response.
  5. Krok testu potwierdzenia jest wykonywany: rzeczywista odpowiedź jest sprawdzana jako przebieg lub Niepowodzenie w zależności od oczekiwanej odpowiedzi.The Assert test step is executed: The actual response is validated as a pass or fail based on an expected response.
  6. Proces jest kontynuowany, dopóki wszystkie testy nie zostaną wykonane.The process continues until all of the tests are executed.
  7. Wyniki testu są zgłaszane.The test results are reported.

Test hosta sieci Web jest zwykle skonfigurowany inaczej niż normalny host sieci Web aplikacji dla przebiegów testowych.Usually, the test web host is configured differently than the app's normal web host for the test runs. Na przykład dla testów można użyć innej bazy danych lub różnych ustawień aplikacji.For example, a different database or different app settings might be used for the tests.

Składniki infrastruktury, takie jak test hosta sieci Web i serwer testu w pamięci (TestServer), są udostępniane lub zarządzane przez pakiet Microsoft. AspNetCore. MVC. test .Infrastructure components, such as the test web host and in-memory test server (TestServer), are provided or managed by the Microsoft.AspNetCore.Mvc.Testing package. Użycie tego pakietu usprawnia tworzenie i wykonywanie testów.Use of this package streamlines test creation and execution.

Pakiet Microsoft.AspNetCore.Mvc.Testing obsługuje następujące zadania:The Microsoft.AspNetCore.Mvc.Testing package handles the following tasks:

  • Kopiuje plik zależności ( . deps) z SUT do katalogu bin projektu testowego.Copies the dependencies file (.deps) from the SUT into the test project's bin directory.
  • Ustawia katalog główny zawartości dla katalogu głównego projektu SUT, aby umożliwić znalezienie plików statycznych i stron/widoków podczas wykonywania testów.Sets the content root to the SUT's project root so that static files and pages/views are found when the tests are executed.
  • Udostępnia klasę WebApplicationFactory do usprawnienia uruchamiania SUT przy użyciu TestServer.Provides the WebApplicationFactory class to streamline bootstrapping the SUT with TestServer.

Dokumentacja testów jednostkowych opisuje sposób konfigurowania projektu testowego i modułu testowego, a także szczegółowe instrukcje dotyczące sposobu uruchamiania testów i zaleceń dotyczących sposobu określania nazw testów i klas testowych.The unit tests documentation describes how to set up a test project and test runner, along with detailed instructions on how to run tests and recommendations for how to name tests and test classes.

Uwaga

Podczas tworzenia projektu testowego dla aplikacji należy oddzielić testy jednostkowe od testów integracji do różnych projektów.When creating a test project for an app, separate the unit tests from the integration tests into different projects. Dzięki temu składniki do testowania infrastruktury nie są przypadkowo uwzględniane w testach jednostkowych.This helps ensure that infrastructure testing components aren't accidentally included in the unit tests. Rozdzielenie testów jednostkowych i integracyjnych umożliwia również kontrolę nad tym, który zestaw testów jest uruchamiany.Separation of unit and integration tests also allows control over which set of tests are run.

Nie istnieje praktycznie żadna różnica między konfiguracją testów aplikacji Razor Pages i aplikacji MVC.There's virtually no difference between the configuration for tests of Razor Pages apps and MVC apps. Jedyną różnicą jest to, jak te testy są nazywane.The only difference is in how the tests are named. W aplikacji Razor Pages testy punktów końcowych stron są zwykle nazywane po klasie modelu strony (na przykład IndexPageTests do testowania integracji składników na stronie indeks).In a Razor Pages app, tests of page endpoints are usually named after the page model class (for example, IndexPageTests to test component integration for the Index page). W aplikacji MVC testy są zwykle zorganizowane według klas kontrolera i nazwane po testowaniu kontrolerów (na przykład HomeControllerTests do integracji składnika testowego dla kontrolera macierzystego).In an MVC app, tests are usually organized by controller classes and named after the controllers they test (for example, HomeControllerTests to test component integration for the Home controller).

Wymagania wstępne aplikacji testowejTest app prerequisites

Projekt testowy musi:The test project must:

Te wymagania wstępne można zobaczyć w przykładowej aplikacji.These prerequisites can be seen in the sample app. Sprawdź plik Tests/RazorPagesProject. Tests/RazorPagesProject. tests. csproj .Inspect the tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj file. Przykładowa aplikacja używa środowiska testowego xUnit i biblioteki analizatora AngleSharp , dzięki czemu Przykładowa aplikacja również odwołuje się do:The sample app uses the xUnit test framework and the AngleSharp parser library, so the sample app also references:

Środowisko SUTSUT environment

Jeśli środowisko SUT nie jest ustawione, środowisko jest domyślnie stosowane do programowania.If the SUT's environment isn't set, the environment defaults to Development.

Podstawowe testy z domyślną WebApplicationFactoryBasic tests with the default WebApplicationFactory

WebApplicationFactory<TEntryPoint > służy do tworzenia TestServer dla testów integracji.WebApplicationFactory<TEntryPoint> is used to create a TestServer for the integration tests. TEntryPoint jest klasą punktu wejścia SUT, zazwyczaj klasy Startup.TEntryPoint is the entry point class of the SUT, usually the Startup class.

Klasy testowe implementują interfejs armatury klasy (IClassFixture) w celu wskazania, że Klasa zawiera testy i udostępnia wystąpienia obiektów udostępnionych w ramach testów w klasie.Test classes implement a class fixture interface (IClassFixture) to indicate the class contains tests and provide shared object instances across the tests in the class.

Następująca Klasa testowa, BasicTests, używa WebApplicationFactory do ładowania SUT i zapewnienia HttpClient do metody testowej, Get_EndpointsReturnSuccessAndCorrectContentType.The following test class, BasicTests, uses the WebApplicationFactory to bootstrap the SUT and provide an HttpClient to a test method, Get_EndpointsReturnSuccessAndCorrectContentType. Metoda sprawdza, czy kod stanu odpowiedzi powiedzie się (kody stanu z zakresu 200-299), a nagłówek Content-Type jest text/html; charset=utf-8 dla kilku stron aplikacji.The method checks if the response status code is successful (status codes in the range 200-299) and the Content-Type header is text/html; charset=utf-8 for several app pages.

Klient tworzy wystąpienie HttpClient, które automatycznie następuje przekierowanie i obsługę plików cookie.CreateClient creates an instance of HttpClient that automatically follows redirects and handles 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 nieistotne pliki cookie nie są zachowywane między żądaniami, gdy zasady zgody Rodo są włączone.By default, non-essential cookies aren't preserved across requests when the GDPR consent policy is enabled. Aby zachować nieistotne pliki cookie, takie jak te używane przez dostawcę TempData, oznacz je jako niezbędne w testach.To preserve non-essential cookies, such as those used by the TempData provider, mark them as essential in your tests. Aby uzyskać instrukcje dotyczące oznaczania pliku cookie jako kluczowego, zobacz podstawowe pliki cookie.For instructions on marking a cookie as essential, see Essential cookies.

Dostosuj WebApplicationFactoryCustomize WebApplicationFactory

Konfigurację hosta sieci Web można utworzyć niezależnie od klas testów przez dziedziczenie z WebApplicationFactory w celu utworzenia jednego lub kilku fabryk niestandardowych:Web host configuration can be created independently of the test classes by inheriting from WebApplicationFactory to create one or more custom factories:

  1. Dziedzicz po WebApplicationFactory i Zastąp ConfigureWebHost.Inherit from WebApplicationFactory and override ConfigureWebHost. IWebHostBuilder umożliwia konfigurację kolekcji usług z ConfigureServices:The IWebHostBuilder allows the configuration of the service collection with ConfigureServices:

    public class CustomWebApplicationFactory<TStartup> 
        : WebApplicationFactory<TStartup> where TStartup: class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                // Create a new service provider.
                var serviceProvider = new ServiceCollection()
                    .AddEntityFrameworkInMemoryDatabase()
                    .BuildServiceProvider();
    
                // Add a database context (ApplicationDbContext) using an in-memory 
                // database for testing.
                services.AddDbContext<ApplicationDbContext>(options => 
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                    options.UseInternalServiceProvider(serviceProvider);
                });
    
                // Build the service provider.
                var sp = services.BuildServiceProvider();
    
                // Create a scope to obtain a reference to the database
                // context (ApplicationDbContext).
                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<ApplicationDbContext>();
                    var logger = scopedServices
                        .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
    
                    // Ensure the database is created.
                    db.Database.EnsureCreated();
    
                    try
                    {
                        // Seed the database with test data.
                        Utilities.InitializeDbForTests(db);
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, "An error occurred seeding the database. Error: {Message}", ex.Message);
                    }
                }
            });
        }
    }
    

    Wypełnianie bazy danych w przykładowej aplikacji jest wykonywane przez metodę InitializeDbForTests.Database seeding in the sample app is performed by the InitializeDbForTests method. Metoda jest opisana w przykładowej testów integracji: sekcja testowa aplikacja w organizacji .The method is described in the Integration tests sample: Test app organization section.

  2. Użyj niestandardowych CustomWebApplicationFactory w klasach testowych.Use the custom CustomWebApplicationFactory in test classes. Poniższy przykład używa fabryki w klasie IndexPageTests:The following example uses the factory in the IndexPageTests class:

    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 aplikacji przykładowej jest skonfigurowany tak, aby uniemożliwiać HttpClient z następujących przekierowań.The sample app's client is configured to prevent the HttpClient from following redirects. Jak wyjaśniono w dalszej części sekcji uwierzytelnianie , pozwala to testom sprawdzić wynik pierwszej odpowiedzi aplikacji.As explained later in the Mock authentication section, this permits tests to check the result of the app's first response. Pierwsza odpowiedź to przekierowanie w wielu z tych testów z nagłówkiem Location.The first response is a redirect in many of these tests with a Location header.

  3. Typowy test używa HttpClient i metod pomocniczych do przetwarzania żądania i odpowiedzi:A typical test uses the HttpClient and helper methods to process the request and the response:

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

Wszelkie żądania POST do SUT muszą być zgodne z sprawdzeniem, czy jest ono automatycznie wykonywane przez system ochrony danych przed fałszerstwem.Any POST request to the SUT must satisfy the antiforgery check that's automatically made by the app's data protection antiforgery system. Aby można było rozmieścić żądanie POST testu, aplikacja testowa musi:In order to arrange for a test's POST request, the test app must:

  1. Utwórz żądanie dla strony.Make a request for the page.
  2. Przeanalizuj plik cookie dotyczący fałszowania i token walidacji żądania z odpowiedzi.Parse the antiforgery cookie and request validation token from the response.
  3. Wprowadź żądanie POST przy użyciu pliku cookie służącego do fałszerstwa i tokenu walidacji żądania.Make the POST request with the antiforgery cookie and request validation token in place.

Metody rozszerzenia pomocnika SendAsync (pomocnicys/HttpClientExtensions. cs) oraz metoda pomocnika GetDocumentAsync (pomocnicys/HtmlHelpers. cs) w przykładowej aplikacji używają analizatora AngleSharp do obsługi kontroli przed fałszerstwem przy użyciu następujących metod:The SendAsync helper extension methods (Helpers/HttpClientExtensions.cs) and the GetDocumentAsync helper method (Helpers/HtmlHelpers.cs) in the sample app use the AngleSharp parser to handle the antiforgery check with the following methods:

  • GetDocumentAsync – odbiera HttpResponseMessage i zwraca IHtmlDocument.GetDocumentAsync – Receives the HttpResponseMessage and returns an IHtmlDocument. GetDocumentAsync używa fabryki przygotowującej odpowiedź wirtualną na podstawie oryginalnego HttpResponseMessage.GetDocumentAsync uses a factory that prepares a virtual response based on the original HttpResponseMessage. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją AngleSharp.For more information, see the AngleSharp documentation.
  • SendAsync metody rozszerzające HttpClient tworzenia HttpRequestMessage i wywołania SendAsync (HttpRequestMessage) w celu przesyłania żądań do SUT.SendAsync extension methods for the HttpClient compose an HttpRequestMessage and call SendAsync(HttpRequestMessage) to submit requests to the SUT. Przeciążenia dla SendAsync akceptują formularz HTML (IHtmlFormElement) i następujące elementy:Overloads for SendAsync accept the HTML form (IHtmlFormElement) and the following:
    • Przycisk przesyłania formularza (IHtmlElement)Submit button of the form (IHtmlElement)
    • Kolekcja wartości formularza (IEnumerable<KeyValuePair<string, string>>)Form values collection (IEnumerable<KeyValuePair<string, string>>)
    • Przycisk Prześlij (IHtmlElement) i wartości formularza (IEnumerable<KeyValuePair<string, string>>)Submit button (IHtmlElement) and form values (IEnumerable<KeyValuePair<string, string>>)

Uwaga

AngleSharp to biblioteka analizy innej firmy używana do celów demonstracyjnych w tym temacie i Przykładowa aplikacja.AngleSharp is a third-party parsing library used for demonstration purposes in this topic and the sample app. AngleSharp nie jest obsługiwana ani wymagana do testowania integracji aplikacji ASP.NET Core.AngleSharp isn't supported or required for integration testing of ASP.NET Core apps. Można użyć innych analizatorów, takich jak pakiet HAP (html).Other parsers can be used, such as the Html Agility Pack (HAP). Innym podejściem jest napisać kod, który będzie obsługiwał token weryfikacji żądań systemu i bezfałszowający plik cookie.Another approach is to write code to handle the antiforgery system's request verification token and antiforgery cookie directly.

Dostosowywanie klienta przy użyciu WithWebHostBuilderCustomize the client with WithWebHostBuilder

Gdy w metodzie testowej jest wymagana dodatkowa konfiguracja, WithWebHostBuilder tworzy nowy WebApplicationFactory z IWebHostBuilder , który jest bardziej dostosowany przez konfigurację.When additional configuration is required within a test method, WithWebHostBuilder creates a new WebApplicationFactory with an IWebHostBuilder that is further customized by configuration.

Metoda testowania Post_DeleteMessageHandler_ReturnsRedirectToRoot przykładowej aplikacji ilustruje użycie WithWebHostBuilder.The Post_DeleteMessageHandler_ReturnsRedirectToRoot test method of the sample app demonstrates the use of WithWebHostBuilder. Ten test służy do usuwania rekordów w bazie danych przez wyzwolenie przesłania formularza w SUT.This test performs a record delete in the database by triggering a form submission in the SUT.

Ponieważ inny test w klasie IndexPageTests wykonuje operację, która usuwa wszystkie rekordy z bazy danych i może być uruchomiona przed metodą Post_DeleteMessageHandler_ReturnsRedirectToRoot, baza danych zostanie oddzielna w tej metodzie testowej, aby upewnić się, że rekord jest obecny dla SUT do usunięcia.Because another test in the IndexPageTests class performs an operation that deletes all of the records in the database and may run before the Post_DeleteMessageHandler_ReturnsRedirectToRoot method, the database is reseeded in this test method to ensure that a record is present for the SUT to delete. Wybranie pierwszego przycisku Usuń formularza messages w SUT jest symulowane w żądaniu do SUT:Selecting the first delete button of the messages form in the SUT is simulated in the request to the 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 klientaClient options

W poniższej tabeli przedstawiono domyślne WebApplicationFactoryClientOptions dostępne podczas tworzenia wystąpień HttpClient.The following table shows the default WebApplicationFactoryClientOptions available when creating HttpClient instances.

OpcjaOption OpisDescription DomyślnyDefault
AllowAutoRedirectAllowAutoRedirect Pobiera lub ustawia czy HttpClient wystąpienia powinny automatycznie śledzić odpowiedzi przekierowania.Gets or sets whether or not HttpClient instances should automatically follow redirect responses. true
BaseAddressBaseAddress Pobiera lub ustawia adres podstawowy HttpClient wystąpień.Gets or sets the base address of HttpClient instances. http://localhost
HandleCookiesHandleCookies Pobiera lub ustawia czy HttpClient wystąpienia powinny obsługiwać pliki cookie.Gets or sets whether HttpClient instances should handle cookies. true
MaxAutomaticRedirectionsMaxAutomaticRedirections Pobiera lub ustawia maksymalną liczbę odpowiedzi przekierowań, które powinny następować HttpClient wystąpieniami.Gets or sets the maximum number of redirect responses that HttpClient instances should follow. 77

Utwórz klasę WebApplicationFactoryClientOptions i przekaż ją do metody onclient (wartości domyślne są pokazane w przykładzie kodu):Create the WebApplicationFactoryClientOptions class and pass it to the CreateClient method (default values are shown in the code example):

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

Wsuń usługi imitacjiInject mock services

Usługi można przesłaniać w teście, używając wywołania ConfigureTestServices na konstruktorze hosta.Services can be overridden in a test with a call to ConfigureTestServices on the host builder. Aby wstrzyknąć usługi makiety, SUT musi mieć klasę Startup z metodą Startup.ConfigureServices.To inject mock services, the SUT must have a Startup class with a Startup.ConfigureServices method.

Przykład SUT obejmuje usługę objętą zakresem zwracającą ofertę.The sample SUT includes a scoped service that returns a quote. Po zażądaniu strony indeksu oferta zostanie osadzona w ukrytym polu na stronie indeksu.The quote is embedded in a hidden field on the Index page when the Index page is requested.

Services/IQuoteService.cs:Services/IQuoteService.cs:

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

Usługi/QuoteService. cs: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:Startup.cs:

services.AddScoped<IQuoteService, QuoteService>();

Pages/index. cshtml. cs: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();
    }

Strony/indeks. cs:Pages/Index.cs:

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

Po uruchomieniu aplikacji SUT jest generowany następujący znacznik:The following markup is generated when the SUT app is run:

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

W celu przetestowania usługi i iniekcji cytatu w teście integracji, usługa makiety jest wstrzykiwana do SUT przez test.To test the service and quote injection in an integration test, a mock service is injected into the SUT by the test. Usługa makiety zastępuje QuoteService aplikacji za pomocą usługi dostarczonej przez aplikację testową o nazwie TestQuoteService:The mock service replaces the app's QuoteService with a service provided by the test app, called TestQuoteService:

IntegrationTests.IndexPageTests.cs: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:ConfigureTestServices is called, and the scoped service is registered:

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

Adiustacje powstające podczas wykonywania testu odzwierciedlają tekst cytatu dostarczony przez TestQuoteService, w związku z czym potwierdzenie przebiega:The markup produced during the test's execution reflects the quote text supplied by TestQuoteService, thus the assertion passes:

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

Uwierzytelnianie w postaci makietyMock authentication

Testy w klasie AuthTests sprawdzają, czy bezpieczny punkt końcowy:Tests in the AuthTests class check that a secure endpoint:

  • Przekierowuje nieuwierzytelnionego użytkownika do strony logowania aplikacji.Redirects an unauthenticated user to the app's Login page.
  • Zwraca zawartość dla uwierzytelnionego użytkownika.Returns content for an authenticated user.

W SUT Strona /SecurePage używa konwencji AuthorizePage w celu zastosowania AuthorizeFilter do strony.In the SUT, the /SecurePage page uses an AuthorizePage convention to apply an AuthorizeFilter to the page. Aby uzyskać więcej informacji, zobacz Razor Pages Konwencji autoryzacji.For more information, see Razor Pages authorization conventions.

services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddRazorPagesOptions(options =>
    {
        options.Conventions.AuthorizePage("/SecurePage");
    });

W teście Get_SecurePageRedirectsAnUnauthenticatedUser WebApplicationFactoryClientOptions jest ustawiony tak, aby nie zezwalać na przekierowania przez ustawienie AllowAutoRedirect na false:In the Get_SecurePageRedirectsAnUnauthenticatedUser test, a WebApplicationFactoryClientOptions is set to disallow redirects by setting AllowAutoRedirect to 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);
}

W przypadku niezezwalania klientowi na śledzenie przekierowania można wykonać następujące operacje:By disallowing the client to follow the redirect, the following checks can be made:

  • Kod stanu zwracany przez SUT można sprawdzić pod kątem oczekiwanego wyniku HttpStatusCode. Redirect , a nie do końcowego kodu stanu po przekierowaniu do strony logowania, która byłaby HttpStatusCode. ok.The status code returned by the SUT can be checked against the expected HttpStatusCode.Redirect result, not the final status code after the redirect to the Login page, which would be HttpStatusCode.OK.
  • Wartość nagłówka Location w nagłówkach odpowiedzi jest sprawdzana w celu potwierdzenia, że rozpoczyna się od http://localhost/Identity/Account/Login, a nie od końcowej odpowiedzi na stronę logowania, gdzie nagłówek Location nie będzie obecny.The Location header value in the response headers is checked to confirm that it starts with http://localhost/Identity/Account/Login, not the final Login page response, where the Location header wouldn't be present.

Aplikacja testowa może zasymulować AuthenticationHandler<TOptions> w ConfigureTestServices , aby przetestować aspekty uwierzytelniania i autoryzacji.The test app can mock an AuthenticationHandler<TOptions> in ConfigureTestServices in order to test aspects of authentication and authorization. Minimalny scenariusz zwraca element AuthenticateResult. Success:A minimal scenario returns an 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);
    }
}

TestAuthHandler jest wywoływana w celu uwierzytelnienia użytkownika, gdy w schemacie uwierzytelniania jest ustawiona wartość Test, gdzie AddAuthentication jest zarejestrowana dla ConfigureTestServices:The TestAuthHandler is called to authenticate a user when the authentication scheme is set to Test where AddAuthentication is registered for ConfigureTestServices:

[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 temat WebApplicationFactoryClientOptions, zobacz sekcję Opcje klienta .For more information on WebApplicationFactoryClientOptions, see the Client options section.

Ustawianie środowiskaSet the environment

Domyślnie środowisko hosta i aplikacji SUT jest skonfigurowane do korzystania ze środowiska deweloperskiego.By default, the SUT's host and app environment is configured to use the Development environment. Aby zastąpić środowisko SUT:To override the SUT's environment:

  • Ustaw zmienną środowiskową ASPNETCORE_ENVIRONMENT (na przykład Staging, Productionlub inną wartość niestandardową, taką jak Testing).Set the ASPNETCORE_ENVIRONMENT environment variable (for example, Staging, Production, or other custom value, such as Testing).
  • Przesłoń CreateHostBuilder w aplikacji testowej, aby odczytać zmienne środowiskowe z prefiksem ASPNETCORE.Override CreateHostBuilder in the test app to read environment variables prefixed with ASPNETCORE.
protected override IHostBuilder CreateHostBuilder() => 
    base.CreateHostBuilder()
        .ConfigureHostConfiguration(
            config => config.AddEnvironmentVariables("ASPNETCORE"));

Jak infrastruktura testowa wnioskuje ścieżkę katalogu głównego zawartości aplikacjiHow the test infrastructure infers the app content root path

Konstruktor WebApplicationFactory wnioskuje ścieżkę katalogu głównego zawartości aplikacji, wyszukując WebApplicationFactoryContentRootAttribute na zestawie zawierającym testy integracji z kluczem równym zestawowi TEntryPoint System.Reflection.Assembly.FullName.The WebApplicationFactory constructor infers the app content root path by searching for a WebApplicationFactoryContentRootAttribute on the assembly containing the integration tests with a key equal to the TEntryPoint assembly System.Reflection.Assembly.FullName. W przypadku nieznalezienia atrybutu o poprawnym kluczu WebApplicationFactory powracanie do wyszukiwania pliku rozwiązania ( . sln) i dołącza nazwę zestawu TEntryPoint do katalogu rozwiązania.In case an attribute with the correct key isn't found, WebApplicationFactory falls back to searching for a solution file (.sln) and appends the TEntryPoint assembly name to the solution directory. Katalog główny aplikacji (ścieżka katalogu głównego zawartości) służy do odnajdywania widoków i plików zawartości.The app root directory (the content root path) is used to discover views and content files.

Wyłącz kopiowanie w tleDisable shadow copying

Kopiowanie w tle powoduje, że testy są wykonywane w innym katalogu niż katalog wyjściowy.Shadow copying causes the tests to execute in a different directory than the output directory. Aby testy działały prawidłowo, kopiowanie w tle musi być wyłączone.For tests to work properly, shadow copying must be disabled. Przykładowa aplikacja używa xUnit i wyłącza kopiowanie w tle dla xUnit, dołączając plik xUnit. Runner. JSON z prawidłowym ustawieniem konfiguracji.The sample app uses xUnit and disables shadow copying for xUnit by including an xunit.runner.json file with the correct configuration setting. Aby uzyskać więcej informacji, zobacz Configuring xUnit with JSON.For more information, see Configuring xUnit with JSON.

Dodaj plik xUnit. Runner. JSON do katalogu głównego projektu testowego z następującą zawartością:Add the xunit.runner.json file to root of the test project with the following content:

{
  "shadowCopy": false
}

Jeśli używasz programu Visual Studio, ustaw wartość właściwości Kopiuj do katalogu wyjściowego na zawsze Kopiuj.If using Visual Studio, set the file's Copy to Output Directory property to Copy always. Jeśli nie korzystasz z programu Visual Studio, Dodaj element docelowy Content do pliku projektu aplikacji testowej:If not using Visual Studio, add a Content target to the test app's project file:

<ItemGroup>
  <Content Update="xunit.runner.json">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </Content>
</ItemGroup>

Usuwanie obiektówDisposal of objects

Po wykonaniu testów implementacji IClassFixture TestServer i HttpClient są usuwane, gdy xUnit usuwa WebApplicationFactory.After the tests of the IClassFixture implementation are executed, TestServer and HttpClient are disposed when xUnit disposes of the WebApplicationFactory. Jeśli obiekty utworzone przez dewelopera wymagają usunięcia, usuń je z implementacji IClassFixture.If objects instantiated by the developer require disposal, dispose of them in the IClassFixture implementation. Aby uzyskać więcej informacji, zobacz implementowanie metody Dispose.For more information, see Implementing a Dispose method.

Przykład testów integracjiIntegration tests sample

Przykładowa aplikacja składa się z dwóch aplikacji:The sample app is composed of two apps:

AplikacjaApp Katalog projektuProject directory OpisDescription
Aplikacja wiadomości (SUT)Message app (the SUT) src/RazorPagesProjectsrc/RazorPagesProject Zezwala użytkownikowi na dodawanie, usuwanie, usuwanie wszystkich i analizowanie komunikatów.Allows a user to add, delete one, delete all, and analyze messages.
Aplikacja testowaTest app tests/RazorPagesProject.Teststests/RazorPagesProject.Tests Służy do integracji testu SUT.Used to integration test the SUT.

Testy można uruchamiać przy użyciu wbudowanych funkcji testowych środowiska IDE, takich jak Visual Studio.The tests can be run using the built-in test features of an IDE, such as Visual Studio. W przypadku używania Visual Studio Code lub wiersza polecenia wykonaj następujące polecenie w wierszu polecenia w katalogu Tests/RazorPagesProject. Tests :If using Visual Studio Code or the command line, execute the following command at a command prompt in the tests/RazorPagesProject.Tests directory:

dotnet test

Organizacja aplikacji wiadomości (SUT)Message app (SUT) organization

SUT to system komunikatów Razor Pages o następujących cechach:The SUT is a Razor Pages message system with the following characteristics:

  • Strona indeks aplikacji (Pages/index. cshtml i Pages/index. cshtml. cs) zawiera metody interfejsu użytkownika i modelu strony umożliwiające sterowanie dodawaniem, usuwaniem i analizą komunikatów (średnia liczba wyrazów na komunikat).The Index page of the app (Pages/Index.cshtml and Pages/Index.cshtml.cs) provides a UI and page model methods to control the addition, deletion, and analysis of messages (average words per message).
  • Komunikat jest opisywany przez klasę Message (Data/Message. cs) z dwiema właściwościami: Id (Key) i Text (Message).A message is described by the Message class (Data/Message.cs) with two properties: Id (key) and Text (message). Właściwość Text jest wymagana i jest ograniczona do 200 znaków.The Text property is required and limited to 200 characters.
  • Komunikaty są przechowywane przy użyciu† bazy danych znajdującej się w pamięci Entity Framework.Messages are stored using Entity Framework's in-memory database†.
  • Aplikacja zawiera warstwę dostępu do danych (DAL) w swojej klasie kontekstu bazy danych AppDbContext (Data/AppDbContext. cs).The app contains a data access layer (DAL) in its database context class, AppDbContext (Data/AppDbContext.cs).
  • Jeśli baza danych jest pusta podczas uruchamiania aplikacji, magazyn komunikatów zostanie zainicjowany przy użyciu trzech komunikatów.If the database is empty on app startup, the message store is initialized with three messages.
  • Aplikacja zawiera /SecurePage dostępną tylko dla uwierzytelnionego użytkownika.The app includes a /SecurePage that can only be accessed by an authenticated user.

†W temacie EF test z niepamięcią, wyjaśniono, jak korzystać z bazy danych w pamięci dla testów z MSTest.†The EF topic, Test with InMemory, explains how to use an in-memory database for tests with MSTest. W tym temacie jest stosowane środowisko testowe xUnit .This topic uses the xUnit test framework. Koncepcje testowe i implementacje testów w różnych strukturach testów są podobne, ale nie są identyczne.Test concepts and test implementations across different test frameworks are similar but not identical.

Mimo że aplikacja nie używa wzorca repozytorium i nie jest skutecznym przykładem wzorca jednostki pracy, Razor Pages obsługuje te wzorce rozwoju.Although the app doesn't use the repository pattern and isn't an effective example of the Unit of Work (UoW) pattern, Razor Pages supports these patterns of development. Aby uzyskać więcej informacji, zobacz projektowanie warstwy trwałości infrastruktury i logiki kontrolera testów (przykład implementuje wzorzec repozytorium).For more information, see Designing the infrastructure persistence layer and Test controller logic (the sample implements the repository pattern).

Testuj organizację aplikacjiTest app organization

Aplikacja testowa to Aplikacja konsolowa w katalogu Tests/RazorPagesProject. Tests .The test app is a console app inside the tests/RazorPagesProject.Tests directory.

Testuj katalog aplikacjiTest app directory OpisDescription
AuthTestsAuthTests Zawiera metody testowe dla:Contains test methods for:
  • Uzyskiwanie dostępu do bezpiecznej strony przez nieuwierzytelniony użytkownik.Accessing a secure page by an unauthenticated user.
  • Uzyskiwanie dostępu do bezpiecznej strony przez uwierzytelnionego użytkownika przy użyciu AuthenticationHandler<TOptions>imitacji.Accessing a secure page by an authenticated user with a mock AuthenticationHandler<TOptions>.
  • Uzyskiwanie profilu użytkownika usługi GitHub i sprawdzanie logowania użytkownika w profilu.Obtaining a GitHub user profile and checking the profile's user login.
BasicTestsBasicTests Zawiera metodę testową dla routingu i typu zawartości.Contains a test method for routing and content type.
IntegrationTestsIntegrationTests Zawiera testy integracji dla strony indeksu przy użyciu niestandardowej klasy WebApplicationFactory.Contains the integration tests for the Index page using custom WebApplicationFactory class.
Pomocnicy/narzędziaHelpers/Utilities
  • Utilities.cs zawiera metodę InitializeDbForTests używaną do wypełniania bazy danych danymi testowymi.Utilities.cs contains the InitializeDbForTests method used to seed the database with test data.
  • HtmlHelpers.cs zapewnia metodę zwracającą IHtmlDocument AngleSharp do użycia przez metody testowe.HtmlHelpers.cs provides a method to return an AngleSharp IHtmlDocument for use by the test methods.
  • HttpClientExtensions.cs zapewniają przeciążenia dla SendAsync do przesyłania żądań do SUT.HttpClientExtensions.cs provide overloads for SendAsync to submit requests to the SUT.

Platforma testowa jest xUnit.The test framework is xUnit. Testy integracji są przeprowadzane przy użyciu programu Microsoft. AspNetCore. TestHost, który obejmuje TestServer.Integration tests are conducted using the Microsoft.AspNetCore.TestHost, which includes the TestServer. Ponieważ pakiet Microsoft. AspNetCore. MVC. test służy do konfigurowania hosta testowego i serwera testowego, pakiety TestHost i TestServer nie wymagają bezpośrednich odwołań do pakietów w pliku projektu aplikacji testowej ani w konfiguracji dewelopera w aplikacji testowej.Because the Microsoft.AspNetCore.Mvc.Testing package is used to configure the test host and test server, the TestHost and TestServer packages don't require direct package references in the test app's project file or developer configuration in the test app.

Umieszczanie bazy danych do testowaniaSeeding the database for testing

Testy integracji zwykle wymagają małego zestawu danych w bazie danych przed wykonaniem testu.Integration tests usually require a small dataset in the database prior to the test execution. Na przykład można usunąć wywołania testu do usuwania rekordów bazy danych, więc baza danych musi mieć co najmniej jeden rekord, aby żądanie usunięcia zakończyło się pomyślnie.For example, a delete test calls for a database record deletion, so the database must have at least one record for the delete request to succeed.

Przykładowa aplikacja odziarnauje bazę danych z trzema komunikatami w Utilities.cs , że testy mogą być używane podczas wykonywania:The sample app seeds the database with three messages in Utilities.cs that tests can use when they execute:

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

Dodatkowe zasobyAdditional resources