Udostępnij za pośrednictwem


Testowanie aplikacji internetowych i usług ASP.NET Core

Napiwek

Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET dostępnych na platformie .NET Docs lub jako bezpłatnego pliku PDF, który można odczytać w trybie offline.

Architektura mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET — miniatura książki eBook.

Kontrolery są centralną częścią dowolnej usługi interfejsu API platformy ASP.NET Core i ASP.NET aplikacji internetowej MVC. W związku z tym należy mieć pewność, że zachowują się zgodnie z oczekiwaniami aplikacji. Testy automatyczne mogą zapewnić pewność siebie i mogą wykrywać błędy przed dotarciem do środowiska produkcyjnego.

Należy przetestować zachowanie kontrolera na podstawie prawidłowych lub nieprawidłowych danych wejściowych, a odpowiedzi kontrolera testów na podstawie wyniku wykonywanej operacji biznesowej. Jednak należy mieć następujące typy testów dla mikrousług:

  • Testy jednostkowe. Te testy zapewniają, że poszczególne składniki aplikacji działają zgodnie z oczekiwaniami. Asercji testuje interfejs API składnika.

  • Testy integracji. Te testy zapewniają, że interakcje składników działają zgodnie z oczekiwaniami względem artefaktów zewnętrznych, takich jak bazy danych. Asercji mogą testować interfejs API składników, interfejs użytkownika lub skutki uboczne akcji, takich jak operacje we/wy bazy danych, rejestrowanie itp.

  • Testy funkcjonalne dla każdej mikrousługi. Te testy zapewniają, że aplikacja działa zgodnie z oczekiwaniami z perspektywy użytkownika.

  • Testy usług. Te testy zapewniają, że kompleksowe przypadki użycia usługi, w tym testowanie wielu usług w tym samym czasie, są testowane. W przypadku tego typu testowania należy najpierw przygotować środowisko. W takim przypadku oznacza to uruchomienie usług (na przykład przy użyciu narzędzia docker-compose up).

Implementowanie testów jednostkowych dla internetowych interfejsów API platformy ASP.NET Core

Testowanie jednostkowe obejmuje testowanie części aplikacji w izolacji od jej infrastruktury i zależności. W przypadku logiki kontrolera testów jednostkowych tylko zawartość pojedynczej akcji lub metody jest testowana, a nie zachowanie jego zależności lub samej struktury. Testy jednostkowe nie wykrywają problemów w interakcji między składnikami — jest to cel testowania integracji.

Podczas testowania jednostkowego akcji kontrolera upewnij się, że koncentrujesz się tylko na ich zachowaniu. Test jednostkowy kontrolera unika takich elementów jak filtry, routing lub powiązanie modelu (mapowanie danych żądania do modelu ViewModel lub DTO). Ponieważ koncentrują się na testowaniu tylko jednej rzeczy, testy jednostkowe są ogólnie proste do pisania i szybkiego uruchamiania. Dobrze napisany zestaw testów jednostkowych można uruchamiać często bez dużego obciążenia.

Testy jednostkowe są implementowane na podstawie struktur testowych, takich jak xUnit.net, MSTest, Moq lub NUnit. W przypadku przykładowej aplikacji eShopOnContainers używamy klasy xUnit.

Podczas pisania testu jednostkowego dla kontrolera internetowego interfejsu API należy utworzyć wystąpienie klasy kontrolera bezpośrednio przy użyciu nowego słowa kluczowego w języku C#, aby test był uruchamiany tak szybko, jak to możliwe. W poniższym przykładzie pokazano, jak to zrobić w przypadku korzystania z narzędzia xUnit jako struktury testowej.

[Fact]
public async Task Get_order_detail_success()
{
    //Arrange
    var fakeOrderId = "12";
    var fakeOrder = GetFakeOrder();

    //...

    //Act
    var orderController = new OrderController(
        _orderServiceMock.Object,
        _basketServiceMock.Object,
        _identityParserMock.Object);

    orderController.ControllerContext.HttpContext = _contextMock.Object;
    var actionResult = await orderController.Detail(fakeOrderId);

    //Assert
    var viewResult = Assert.IsType<ViewResult>(actionResult);
    Assert.IsAssignableFrom<Order>(viewResult.ViewData.Model);
}

Implementowanie testów integracji i funkcjonalności dla każdej mikrousługi

Jak wspomniano, testy integracji i testy funkcjonalne mają różne cele i cele. Jednak sposób implementacji obu tych elementów podczas testowania kontrolerów ASP.NET Core jest podobny, dlatego w tej sekcji skoncentrujemy się na testach integracji.

Testowanie integracji gwarantuje, że składniki aplikacji działają prawidłowo podczas montażu. ASP.NET Core obsługuje testowanie integracji przy użyciu struktur testów jednostkowych i wbudowanego hosta internetowego testowego, który może służyć do obsługi żądań bez obciążenia sieciowego.

W przeciwieństwie do testowania jednostkowego testy integracji często obejmują problemy z infrastrukturą aplikacji, takie jak baza danych, system plików, zasoby sieciowe lub żądania internetowe i odpowiedzi. Testy jednostkowe używają fałszywych lub makiety obiektów zamiast tych problemów. Jednak celem testów integracji jest potwierdzenie, że system działa zgodnie z oczekiwaniami w przypadku tych systemów, więc do testowania integracji nie używasz fałszywych lub pozorujących obiektów. Zamiast tego należy uwzględnić infrastrukturę, na przykład dostęp do bazy danych lub wywołanie usługi z innych usług.

Ponieważ testy integracji wykonują większe segmenty kodu niż testy jednostkowe i dlatego, że testy integracji opierają się na elementach infrastruktury, zwykle są to rzędy wielkości wolniejsze niż testy jednostkowe. W związku z tym dobrym pomysłem jest ograniczenie liczby testów integracji, które piszesz i uruchamiasz.

ASP.NET Core zawiera wbudowany testowy host internetowy, który może służyć do obsługi żądań HTTP bez obciążenia sieciowego, co oznacza, że można uruchamiać te testy szybciej niż podczas korzystania z rzeczywistego hosta internetowego. Testowy host internetowy (TestServer) jest dostępny w składniku NuGet jako Microsoft.AspNetCore.TestHost. Można go dodać do projektów testowych integracji i użyć do hostowania aplikacji ASP.NET Core.

Jak widać w poniższym kodzie, podczas tworzenia testów integracji dla kontrolerów ASP.NET Core tworzy się wystąpienia kontrolerów za pośrednictwem hosta testowego. Ta funkcja jest porównywalna z żądaniem HTTP, ale działa szybciej.

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

Dodatkowe zasoby

Implementowanie testów usług w aplikacji z wieloma kontenerami

Jak wspomniano wcześniej, podczas testowania aplikacji wielokontenerowych wszystkie mikrousługi muszą być uruchomione w ramach hosta platformy Docker lub klastra kontenerów. Kompleksowe testy usług, które obejmują wiele operacji obejmujących kilka mikrousług, wymagają wdrożenia i uruchomienia całej aplikacji na hoście platformy Docker przez uruchomienie narzędzia docker-compose up (lub porównywalnego mechanizmu, jeśli używasz orkiestratora). Po uruchomieniu całej aplikacji i wszystkich jej usług można wykonać kompleksowe testy integracji i funkcjonalności.

Istnieje kilka metod, których można użyć. W pliku docker-compose.yml używanym do wdrożenia aplikacji na poziomie rozwiązania można rozwinąć punkt wejścia, aby użyć testu dotnet. Możesz również użyć innego pliku redagowania, który będzie uruchamiał testy na obrazie, którego dotyczysz. Używając innego pliku redagowania do testów integracji, które obejmują mikrousługi i bazy danych w kontenerach, możesz upewnić się, że powiązane dane są zawsze resetowane do pierwotnego stanu przed uruchomieniem testów.

Gdy aplikacja redagowania jest uruchomiona, możesz skorzystać z punktów przerwania i wyjątków, jeśli używasz programu Visual Studio. Możesz też automatycznie uruchamiać testy integracji w potoku ciągłej integracji w usłudze Azure DevOps Services lub dowolnym innym systemie ciągłej integracji/ciągłego wdrażania, który obsługuje kontenery platformy Docker.

Testowanie w eShopOnContainers

Testy aplikacji referencyjnej (eShopOnContainers) zostały niedawno zrestrukturyzowane, a teraz istnieją cztery kategorie:

  1. Testy jednostkowe, zwykłe zwykłe regularne testy jednostkowe, zawarte w elemencie {MicroserviceName}. Projekty UnitTests

  2. Testy funkcjonalne/integracyjne mikrousług, z przypadkami testowymi obejmującymi infrastrukturę dla każdej mikrousługi, ale odizolowaną od innych i znajdują się w elemencie {MicroserviceName}. Projekty FunctionalTests .

  3. Testy funkcjonalne/integracyjne aplikacji, które koncentrują się na integracji mikrousług z przypadkami testowymi, które wywierają kilka mikrousług. Te testy znajdują się w projekcie Application.FunctionalTests.

Testy jednostkowe i integracyjne są zorganizowane w folderze testowym w projekcie mikrousługi, natomiast testy aplikacji i obciążenia są zarządzane oddzielnie w folderze głównym, jak pokazano na rysunku 6–25.

Zrzut ekranu programu VS wskazujący niektóre projekty testowe w rozwiązaniu.

Rysunek 6–25. Testowanie struktury folderów w eShopOnContainers

Testy funkcjonalne/integracyjne mikrousług i aplikacji są uruchamiane z poziomu programu Visual Studio przy użyciu zwykłego modułu uruchamiającego testy, ale najpierw należy uruchomić wymagane usługi infrastruktury z zestawem plików docker-compose zawartych w folderze testowym rozwiązania:

docker-compose-test.yml

version: '3.4'

services:
  redis.data:
    image: redis:alpine
  rabbitmq:
    image: rabbitmq:3-management-alpine
  sqldata:
    image: mcr.microsoft.com/mssql/server:2017-latest
  nosqldata:
    image: mongo

docker-compose-test.override.yml

version: '3.4'

services:
  redis.data:
    ports:
      - "6379:6379"
  rabbitmq:
    ports:
      - "15672:15672"
      - "5672:5672"
  sqldata:
    environment:
      - SA_PASSWORD=[PLACEHOLDER]
      - ACCEPT_EULA=Y
    ports:
      - "5433:1433"
  nosqldata:
    ports:
      - "27017:27017"

Ważne

Firma Microsoft zaleca korzystanie z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Jeśli łączysz się z usługą Azure SQL, tożsamości zarządzane dla zasobów platformy Azure to zalecana metoda uwierzytelniania.

Aby więc uruchomić testy funkcjonalne/integracyjne, należy najpierw uruchomić to polecenie z folderu testowego rozwiązania:

docker-compose -f docker-compose-test.yml -f docker-compose-test.override.yml up

Jak widać, te pliki docker-compose uruchamiają tylko mikrousługi Redis, RabbitMQ, SQL Server i MongoDB.

Dodatkowe zasoby