Testowanie w ASP.NET Core integracjiIntegration testing in ASP.NET Core

Przez Steve SmithBy Steve Smith

Testowanie integracji gwarantuje, że składniki aplikacji działać prawidłowo, jeśli połączone ze sobą.Integration testing ensures that an application's components function correctly when assembled together. Integracja obsługuje platformy ASP.NET Core testowania przy użyciu platform testów jednostkowych i wbudowane testu hosta sieci web, który może służyć do obsługi żądań bez obciążenie sieci.ASP.NET Core supports integration testing using unit test frameworks and a built-in test web host that can be used to handle requests without network overhead.

Wyświetlić lub pobrać przykładowy kod (sposobu pobierania)View or download sample code (how to download)

Wprowadzenie do integracji testowaniaIntroduction to integration testing

Testy integracji Sprawdź, czy różne części aplikacji poprawnie współpracują ze sobą.Integration tests verify that different parts of an application work correctly together. W odróżnieniu od testy jednostkowe, integracja testy obejmują często dotyczy infrastruktury aplikacji, takich jak bazy danych, system plików, zasobów sieciowych lub żądania sieci web i odpowiedzi.Unlike Unit testing, integration tests frequently involve application infrastructure concerns, such as a database, file system, network resources, or web requests and responses. Testy jednostkowe używać elementów sztucznych lub zasymulować obiektów zamiast tych problemów, ale testy integracji ma na celu potwierdzenie, że system działa zgodnie z oczekiwaniami do tych systemów.Unit tests use fakes or mock objects in place of these concerns, but the purpose of integration tests is to confirm that the system works as expected with these systems.

Testy integracji, ponieważ korzystają oni większych segmenty kodu i opierają się na elementy infrastruktury zazwyczaj rzędów wolniej niż testy jednostkowe można.Integration tests, because they exercise larger segments of code and because they rely on infrastructure elements, tend to be orders of magnitude slower than unit tests. W związku z tym jest dobrym rozwiązaniem jest ograniczenie ile testów integracji zapisu, zwłaszcza, jeśli takie samo zachowanie można przetestować z testu jednostkowego.Thus, it's a good idea to limit how many integration tests you write, especially if you can test the same behavior with a unit test.

Uwaga

Jeśli niektóre zachowanie można przetestować przy użyciu testu jednostkowego lub testu integracji, preferowane testu jednostkowego, ponieważ będzie prawie zawsze można szybciej.If some behavior can be tested using either a unit test or an integration test, prefer the unit test, since it will be almost always be faster. Konieczne może być dziesiątki lub setki testy jednostek z wielu różnych danych wejściowych, ale tylko kilka najważniejsze scenariusze obejmujące testów integracji.You might have dozens or hundreds of unit tests with many different inputs but just a handful of integration tests covering the most important scenarios.

Testowanie logiki w ramach własnego metod jest zwykle domeny testów jednostkowych.Testing the logic within your own methods is usually the domain of unit tests. Testowanie, jak aplikacja działa w ramach, na przykład z platformy ASP.NET Core lub z bazą danych jest gdzie testów integracji wchodzić w grę.Testing how your application works within its framework, for example with ASP.NET Core, or with a database is where integration tests come into play. Nie wymaga zbyt wiele testów integracji, aby upewnić się, że jesteś w stanie zapisanie wiersza do bazy danych i ponownie go odczytać.It doesn't take too many integration tests to confirm that you're able to write a row to the database and read it back. Nie należy przetestować każdej permutacji możliwe kodu dostępu do danych — należy przetestować za mało, aby mieć pewność, że aplikacja działa prawidłowo.You don't need to test every possible permutation of your data access code - you only need to test enough to give you confidence that your application is working properly.

Testowanie integracji platformy ASP.NET CoreIntegration testing ASP.NET Core

Aby uzyskać ustawione do integracji wykonywania testów, należy utworzyć projektu testowego, Dodaj odwołanie do projektu sieci web platformy ASP.NET Core i zainstaluj moduł uruchamiający.To get set up to run integration tests, you'll need to create a test project, add a reference to your ASP.NET Core web project, and install a test runner. Ten proces jest opisany w testy jednostkowe dokumentacji wraz z bardziej szczegółowe instrukcje na temat uruchamiania testów i zalecenia dotyczące nazewnictwa sieci testy i klasy testowe.This process is described in the Unit testing documentation, along with more detailed instructions on running tests and recommendations for naming your tests and test classes.

Uwaga

Oddzielanie testy jednostkowe i integracji testów przy użyciu różnych projektów.Separate your unit tests and your integration tests using different projects. To pomaga zapewnić, że nie przypadkowego wprowadzenia zastrzeżenia co do infrastruktury do testy jednostkowe i pozwala łatwo wybrać zestaw testów do uruchomienia.This helps ensure you don't accidentally introduce infrastructure concerns into your unit tests and lets you easily choose which set of tests to run.

Host testówThe Test Host

Platformy ASP.NET Core obejmuje hosta testów, który można dodać do integracji projekty testowe, a używany do hostowania platformy ASP.NET Core aplikacji, test obsługujących żądania bez konieczności hosta sieci web prawdziwe.ASP.NET Core includes a test host that can be added to integration test projects and used to host ASP.NET Core applications, serving test requests without the need for a real web host. Dostarczona próbka zawiera projekt testowy integracji, który został skonfigurowany do używania xUnit i hosta testów.The provided sample includes an integration test project which has been configured to use xUnit and the Test Host. Używa Microsoft.AspNetCore.TestHost pakietu NuGet.It uses the Microsoft.AspNetCore.TestHost NuGet package.

Raz Microsoft.AspNetCore.TestHost pakietu jest dołączony do projektu, można tworzyć i konfigurować TestServer w testach.Once the Microsoft.AspNetCore.TestHost package is included in the project, you'll be able to create and configure a TestServer in your tests. Następującego testu przedstawiono sposób sprawdzania, czy żądania skierowane do katalogu głównego witryny zwraca "Witaj świecie!"The following test shows how to verify that a request made to the root of a site returns "Hello World!" i uruchamiać pomyślnie przed domyślnie sieci Web platformy ASP.NET Core pusty szablon utworzony przez program Visual Studio.and should run successfully against the default ASP.NET Core Empty Web template created by Visual Studio.

public class PrimeWebDefaultRequestShould
{
    private readonly TestServer _server;
    private readonly HttpClient _client;
    public PrimeWebDefaultRequestShould()
    {
        // Arrange
        _server = new TestServer(new WebHostBuilder()
            .UseStartup<Startup>());
        _client = _server.CreateClient();
    }

    [Fact]
    public async Task ReturnHelloWorld()
    {
        // Act
        var response = await _client.GetAsync("/");
        response.EnsureSuccessStatusCode();

        var responseString = await response.Content.ReadAsStringAsync();

        // Assert
        Assert.Equal("Hello World!",
            responseString);
    }
}

Ten test jest przy użyciu wzorca Assert Act Rozmieść.This test is using the Arrange-Act-Assert pattern. Krok Rozmieść odbywa się w konstruktorze, która tworzy wystąpienie TestServer.The Arrange step is done in the constructor, which creates an instance of TestServer. Skonfigurowany WebHostBuilder będzie używane do tworzenia TestHost; w tym przykładzie Configure metody z systemu w ramach testu (SUT) Startup klasy jest przekazywana do WebHostBuilder.A configured WebHostBuilder will be used to create a TestHost; in this example, the Configure method from the system under test (SUT)'s Startup class is passed to the WebHostBuilder. Ta metoda będzie służyć do konfigurowania potoku żądania z TestServer do tak samo jak serwer SUT może być skonfigurowany.This method will be used to configure the request pipeline of the TestServer identically to how the SUT server would be configured.

W części Act testu, żądań do TestServer wystąpienia dla ścieżki "/" i odpowiedź jest do odczytu do ciągu.In the Act portion of the test, a request is made to the TestServer instance for the "/" path, and the response is read back into a string. Ten ciąg jest porównywana z oczekiwany ciąg "Hello World!".This string is compared with the expected string of "Hello World!". Jeśli są zgodne, test przechodzi poprawnie; w przeciwnym razie awarii.If they match, the test passes; otherwise, it fails.

Teraz można dodać kilka testy dodatkowe integracji, aby upewnić się, że pierwsze sprawdzanie funkcji działa za pośrednictwem aplikacji sieci web:Now you can add a few additional integration tests to confirm that the prime checking functionality works via the web application:

public class PrimeWebCheckPrimeShould
{
    private readonly TestServer _server;
    private readonly HttpClient _client;
    public PrimeWebCheckPrimeShould()
    {
        // Arrange
        _server = new TestServer(new WebHostBuilder()
            .UseStartup<Startup>());
        _client = _server.CreateClient();
    }

    private async Task<string> GetCheckPrimeResponseString(
        string querystring = "")
    {
        var request = "/checkprime";
        if(!string.IsNullOrEmpty(querystring))
        {
            request += "?" + querystring;
        }
        var response = await _client.GetAsync(request);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadAsStringAsync();
    }

    [Fact]
    public async Task ReturnInstructionsGivenEmptyQueryString()
    {
        // Act
        var responseString = await GetCheckPrimeResponseString();

        // Assert
        Assert.Equal("Pass in a number to check in the form /checkprime?5",
            responseString);
    }
    [Fact]
    public async Task ReturnPrimeGiven5()
    {
        // Act
        var responseString = await GetCheckPrimeResponseString("5");

        // Assert
        Assert.Equal("5 is prime!",
            responseString);
    }

    [Fact]
    public async Task ReturnNotPrimeGiven6()
    {
        // Act
        var responseString = await GetCheckPrimeResponseString("6");

        // Assert
        Assert.Equal("6 is NOT prime!",
            responseString);
    }
}

Należy pamiętać, że nie naprawdę próbujesz testu poprawności sprawdzania liczba pierwsza z tych testów, ale zamiast aplikacji sieci web wykonuje oczekiwań.Note that you're not really trying to test the correctness of the prime number checker with these tests but rather that the web application is doing what you expect. Masz już pokrycie testu jednostki, który daje pewność działania w PrimeService, jak pokazano poniżej:You already have unit test coverage that gives you confidence in PrimeService, as you can see here:

Eksplorator testów

Dowiedz się więcej o testów jednostkowych w testy jednostkowe artykułu.You can learn more about the unit tests in the Unit testing article.

Testowanie Mvc i Razor integracjiIntegration testing Mvc/Razor

Projekty testowe, które zawierają widokami Razor wymagają <PreserveCompilationContext> można ustawić wartości true w .csproj pliku:Test projects that contain Razor views require <PreserveCompilationContext> be set to true in the .csproj file:

    <PreserveCompilationContext>true</PreserveCompilationContext>

Projekty tego elementu zostanie wygenerowany błąd podobny do następującego:Projects missing this element will generate an error similar to the following:

Microsoft.AspNetCore.Mvc.Razor.Compilation.CompilationFailedException: 'One or more compilation failures occurred:
ooebhccx.1bd(4,62): error CS0012: The type 'Attribute' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.

Refaktoryzacja do używania oprogramowania pośredniczącegoRefactoring to use middleware

Refaktoryzacja jest proces zmiany kodu aplikacji, aby zwiększyć jego projekt bez zmiany zachowania.Refactoring is the process of changing an application's code to improve its design without changing its behavior. W idealnym przypadku należy zrobić po mechanizm przekazywania testów, ponieważ te upewnij się, że zachowanie systemu nie zmienia się przed i po zmianie.It should ideally be done when there is a suite of passing tests, since these help ensure the system's behavior remains the same before and after the changes. Trwa wyszukiwanie w taki sposób, w którym zaimplementowano pierwsze sprawdzanie logikę w aplikacji sieci web Configure metody, zobacz:Looking at the way in which the prime checking logic is implemented in the web application's Configure method, you see:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.Run(async (context) =>
    {
        if (context.Request.Path.Value.Contains("checkprime"))
        {
            int numberToCheck;
            try
            {
                numberToCheck = int.Parse(context.Request.QueryString.Value.Replace("?", ""));
                var primeService = new PrimeService();
                if (primeService.IsPrime(numberToCheck))
                {
                    await context.Response.WriteAsync($"{numberToCheck} is prime!");
                }
                else
                {
                    await context.Response.WriteAsync($"{numberToCheck} is NOT prime!");
                }
            }
            catch
            {
                await context.Response.WriteAsync("Pass in a number to check in the form /checkprime?5");
            }
        }
        else
        {
            await context.Response.WriteAsync("Hello World!");
        }
    });
}

Ten kod działa, ale jest daleko od sposób wykonania tego rodzaju funkcji w aplikacji platformy ASP.NET Core nawet tak proste, jak jest to.This code works, but it's far from how you would like to implement this kind of functionality in an ASP.NET Core application, even as simple as this is. Wyobraź sobie co Configure metoda będzie wyglądać w razie potrzeby można dodać do niego tyle kodu, za każdym razem Dodaj inny adres URL punktu końcowego!Imagine what the Configure method would look like if you needed to add this much code to it every time you add another URL endpoint!

Co należy rozważyć możliwość polega na dodaniu MVC do aplikacji i Tworzenie kontrolera w celu obsługi podstawowe sprawdzanie.One option to consider is adding MVC to the application and creating a controller to handle the prime checking. Jednak przy założeniu, że nie zostanie muszą inne funkcje MVC, które są nieco overkill.However, assuming you don't currently need any other MVC functionality, that's a bit overkill.

Możesz można jednak korzystać z platformy ASP.NET Core oprogramowanie pośredniczące, która pomoże nam Hermetyzowanie pierwsze sprawdzanie logikę w jej własnej klasy i osiągnięcia lepszego separacji w Configure Metoda.You can, however, take advantage of ASP.NET Core middleware, which will help us encapsulate the prime checking logic in its own class and achieve better separation of concerns in the Configure method.

Chcesz zezwolić na ścieżce używa oprogramowania pośredniczącego należy określić jako parametru, więc oczekuje klasy oprogramowanie pośredniczące RequestDelegate i PrimeCheckerOptions wystąpienie w jego konstruktora.You want to allow the path the middleware uses to be specified as a parameter, so the middleware class expects a RequestDelegate and a PrimeCheckerOptions instance in its constructor. Jeśli ścieżka żądania nie pasuje to oprogramowanie pośredniczące jest skonfigurowany, można oczekiwać, wystarczy wywołać następne oprogramowanie pośredniczące w łańcuchu i nic nie rób dalszych.If the path of the request doesn't match what this middleware is configured to expect, you simply call the next middleware in the chain and do nothing further. Pozostała część kod implementacji, który znajdował się w Configure znajduje się teraz w Invoke metody.The rest of the implementation code that was in Configure is now in the Invoke method.

Uwaga

Ponieważ zależy od oprogramowania pośredniczącego PrimeService usługi, jest również żąda wystąpienie tej usługi z konstruktora.Since the middleware depends on the PrimeService service, you're also requesting an instance of this service with the constructor. Platformę zapewni tej usługi za pośrednictwem iniekcji zależności, zakładając, że został on skonfigurowany, na przykład w ConfigureServices.The framework will provide this service via Dependency Injection, assuming it has been configured, for example in ConfigureServices.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using PrimeWeb.Services;
using System;
using System.Threading.Tasks;

namespace PrimeWeb.Middleware
{
    public class PrimeCheckerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly PrimeCheckerOptions _options;
        private readonly PrimeService _primeService;

        public PrimeCheckerMiddleware(RequestDelegate next,
            PrimeCheckerOptions options,
            PrimeService primeService)
        {
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (primeService == null)
            {
                throw new ArgumentNullException(nameof(primeService));
            }

            _next = next;
            _options = options;
            _primeService = primeService;
        }

        public async Task Invoke(HttpContext context)
        {
            var request = context.Request;
            if (!request.Path.HasValue ||
                request.Path != _options.Path)
            {
                await _next.Invoke(context);
            }
            else
            {
                int numberToCheck;
                if (int.TryParse(request.QueryString.Value.Replace("?", ""), out numberToCheck))
                {
                    if (_primeService.IsPrime(numberToCheck))
                    {
                        await context.Response.WriteAsync($"{numberToCheck} is prime!");
                    }
                    else
                    {
                        await context.Response.WriteAsync($"{numberToCheck} is NOT prime!");
                    }
                }
                else
                {
                    await context.Response.WriteAsync($"Pass in a number to check in the form {_options.Path}?5");
                }
            }
        }
    }
}

Ponieważ to oprogramowanie pośredniczące działa jako punkt końcowy w łańcuchu delegata żądania, gdy jest zgodna z jego ścieżki, nie jest Brak wywołania _next.Invoke podczas tego oprogramowania pośredniczącego obsługuje żądanie.Since this middleware acts as an endpoint in the request delegate chain when its path matches, there is no call to _next.Invoke when this middleware handles the request.

Z tego oprogramowania pośredniczącego w miejscu i niektóre przydatne metody rozszerzenia utworzone, aby ułatwić jego konfigurowania, refactored Configure metody wygląda następująco:With this middleware in place and some helpful extension methods created to make configuring it easier, the refactored Configure method looks like this:

        IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UsePrimeChecker();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

Po tym refaktoryzacji masz pewność, że aplikacja sieci web wciąż działa jak poprzednio, ponieważ testy integracji wszystkich przekazywanie.Following this refactoring, you're confident that the web application still works as before, since your integration tests are all passing.

Uwaga

Jest dobrym pomysłem jest Zatwierdź zmiany do kontroli źródła, po zakończeniu refaktoryzacji i Twoje testy zostały zaliczone pomyślnie.It's a good idea to commit your changes to source control after you complete a refactoring and your tests pass. Jeśli jest ćwiczenia testu Driven Development należy rozważyć dodanie zatwierdzania do cykl czerwony-zielony-Zrefaktoryzuj.If you're practicing Test Driven Development, consider adding Commit to your Red-Green-Refactor cycle.

ResourcesResources