Testar serviços e aplicativos Web do ASP.NET CoreTesting ASP.NET Core services and web apps

Os controladores são uma parte central de qualquer serviço de API do ASP.NET Core e aplicativo Web ASP.NET MVC.Controllers are a central part of any ASP.NET Core API service and ASP.NET MVC Web application. Assim, você precisa estar confiante de que eles se comportarão conforme o esperado para o aplicativo.As such, you should have confidence they behave as intended for your application. Testes automatizados podem fornecer essa confiança e detectar erros antes que eles atinjam a produção.Automated tests can provide you with this confidence and can detect errors before they reach production.

É necessário testar como o controlador se comporta com base em entradas válidas ou inválidas, bem como testar suas respostas com base nos resultados da operação de negócios realizada.You need to test how the controller behaves based on valid or invalid inputs, and test controller responses based on the result of the business operation it performs. No entanto, você deveria ter esses tipos de testes nos seus microsserviços:However, you should have these types of tests for your microservices:

  • Testes de unidade.Unit tests. Eles garantem que os componentes individuais do aplicativo funcionam como esperado.These ensure that individual components of the application work as expected. Instruções assert testam a API do componente.Assertions test the component API.

  • Testes de integração.Integration tests. Garantem que as interações do componente funcionam como esperado em artefatos externos, como bancos de dados.These ensure that component interactions work as expected against external artifacts like databases. Instruções assert podem testar a API do componente, a interface do usuário ou os efeitos colaterais de ações, como E/S de banco de dados, registros em log etc.Assertions can test component API, UI, or the side effects of actions like database I/O, logging, etc.

  • Testes funcionais para cada microsserviço.Functional tests for each microservice. Garantem que o aplicativo funciona conforme o esperado da perspectiva do usuário.These ensure that the application works as expected from the user’s perspective.

  • Testes de serviço.Service tests. Garantem que os casos de uso do serviço de ponta a ponta sejam testados, incluindo testes de vários serviços ao mesmo tempo.These ensure that end-to-end service use cases, including testing multiple services at the same time, are tested. Para esse tipo de teste, é necessário preparar o ambiente primeiro.For this type of testing, you need to prepare the environment first. Nesse caso, significa iniciar os serviços (por exemplo, usando o docker-compose up).In this case, it means starting the services (for example, by using docker-compose up).

Implementar testes de unidade para APIs Web do ASP.NET CoreImplementing unit tests for ASP.NET Core Web APIs

O teste de unidade envolve o teste de uma parte de um aplicativo em isolamento de sua infraestrutura e suas dependências.Unit testing involves testing a part of an application in isolation from its infrastructure and dependencies. Ao executar a lógica do controlador de teste de unidade, somente o conteúdo de uma única ação ou método é testado, e não o comportamento de suas dependências ou da estrutura.When you unit test controller logic, only the content of a single action or method is tested, not the behavior of its dependencies or of the framework itself. Os testes de unidade não detectam problemas na interação entre componentes, o que é a finalidade dos testes de integração.Unit tests do not detect issues in the interaction between components—that is the purpose of integration testing.

Ao executar o teste de unidade nas ações do controlador, concentre-se apenas em seu comportamento.As you unit test your controller actions, make sure you focus only on their behavior. Um teste de unidade de controlador evita itens como filtros, roteamento ou model binding (o mapeamento dos dados de solicitação a um ViewModel ou DTO).A controller unit test avoids things like filters, routing, or model binding (the mapping of request data to a ViewModel or DTO). Ao se concentrarem no teste de apenas um item, os testes de unidade geralmente são simples de serem gravados e rápidos de serem executados.Because they focus on testing just one thing, unit tests are generally simple to write and quick to run. Um conjunto bem escrito de testes de unidade pode ser executado com frequência sem muita sobrecarga.A well-written set of unit tests can be run frequently without much overhead.

Testes de unidade são implementados com base em estruturas de teste como xUnit.net, MSTest, Moq ou NUnit.Unit tests are implemented based on test frameworks like xUnit.net, MSTest, Moq, or NUnit. Para o aplicativo de exemplo eShopOnContainers, estamos usando o xUnit.For the eShopOnContainers sample application, we are using xUnit.

Ao gravar um teste de unidade de um controlador de API Web, você cria instâncias da classe do controlador usando diretamente a nova palavra-chave no C#, para que o teste seja executado o mais rápido possível.When you write a unit test for a Web API controller, you instantiate the controller class directly using the new keyword in C#, so that the test will run as fast as possible. O exemplo a seguir mostra como fazer isso usando o xUnit como a estrutura de teste.The following example shows how to do this when using xUnit as the Test framework.

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

Implementar testes funcionais e de integração em cada microsserviçoImplementing integration and functional tests for each microservice

Conforme observado, testes funcionais e de integração têm finalidades e objetivos diferentes.As noted, integration tests and functional tests have different purposes and goals. No entanto, a maneira de implementar ambos ao testar os controladores do ASP.NET Core é semelhante. Nesta seção, o foco é o teste de integração.However, the way you implement both when testing ASP.NET Core controllers is similar, so in this section we concentrate on integration tests.

Testes de integração garantem que os componentes de um aplicativo funcionem corretamente quando montados.Integration testing ensures that an application's components function correctly when assembled. O ASP.NET Core é compatível com testes de integração por meio de estruturas de teste de unidade e um host Web de testes interno que pode ser utilizado para lidar com solicitações sem sobrecarga de rede.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.

Ao contrário do teste de unidade, os testes de integração frequentemente envolvem questões relacionadas com a infraestrutura do aplicativo, como banco de dados, sistema de arquivos, recurso de rede ou solicitações e respostas da Web.Unlike unit testing, integration tests frequently involve application infrastructure concerns, such as a database, file system, network resources, or web requests and responses. Testes de unidade usam objetos falsos ou fictícios em vez disso.Unit tests use fakes or mock objects in place of these concerns. Porém, a finalidade dos testes de integração é confirmar que o sistema funciona como o esperado com esses sistemas. Então, nos testes de integração não se usam objetos falsos ou fictícios.But the purpose of integration tests is to confirm that the system works as expected with these systems, so for integration testing you do not use fakes or mock objects. Em vez disso, você inclui a infraestrutura, como acesso ao banco de dados ou invocação de serviço de outros serviços.Instead, you include the infrastructure, like database access or service invocation from other services.

Como os testes de integração exercitam segmentos de código maiores que os testes de unidade e contam com elementos de infraestrutura, eles tendem a ter ordens de magnitude mais lentas que os testes de unidade.Because integration tests exercise larger segments of code than unit tests, and because integration tests rely on infrastructure elements, they tend to be orders of magnitude slower than unit tests. Portanto, é uma boa ideia limitar a quantidade de testes de integração gravados e executados.Thus, it is a good idea to limit how many integration tests you write and run.

O ASP.NET Core inclui um host Web interno para testes que pode ser usado para lidar com solicitações HTTP sem sobrecarga de rede, o que significa que é possível executar esses testes mais rápido com um host Web real.ASP.NET Core includes a built-in test web host that can be used to handle HTTP requests without network overhead, meaning that you can run those tests faster when using a real web host. O host Web de testes (TestServer) está disponível em um componente NuGet como Microsoft.AspNetCore.TestHost.The test web host (TestServer) is available in a NuGet component as Microsoft.AspNetCore.TestHost. Ele pode ser adicionado a projetos de teste de integração e usados para hospedar aplicativos do ASP.NET Core.It can be added to integration test projects and used to host ASP.NET Core applications.

Conforme mostrado no código a seguir, ao criar testes de integração para controladores do ASP.NET Core, você cria instâncias para os controladores por meio do host de teste.As you can see in the following code, when you create integration tests for ASP.NET Core controllers, you instantiate the controllers through the test host. Isso é equivalente a uma solicitação HTTP, mas ele é executado mais rapidamente.This is comparable to an HTTP request, but it runs faster.

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

Recursos adicionaisAdditional resources

Implementar testes de serviço em um aplicativo com vários contêineresImplementing service tests on a multi-container application

Conforme mencionado anteriormente, ao testar aplicativos com vários contêineres, todos os microsserviços precisam ser executados no host do Docker ou no cluster do contêiner.As noted earlier, when you test multi-container applications, all the microservices need to be running within the Docker host or container cluster. Testes de serviço de ponta a ponta que incluem várias operações envolvendo diversos microsserviços exigem a implantação e inicialização do aplicativo inteiro no host do Docker por meio da execução do docker-compose up (ou um mecanismo semelhante se você estiver usando um orquestrador).End-to-end service tests that include multiple operations involving several microservices require you to deploy and start the whole application in the Docker host by running docker-compose up (or a comparable mechanism if you are using an orchestrator). Quando o aplicativo inteiro e todos os seus serviços estiverem em execução, você pode executar testes funcionais e de integração de ponta a ponta.Once the whole application and all its services is running, you can execute end-to-end integration and functional tests.

Existem algumas abordagens que você pode usar.There are a few approaches you can use. No arquivo docker-compose.yml usado para implantar o aplicativo, é possível expandir o ponto de entrada no nível da solução para usar o dotnet test.In the docker-compose.yml file that you use to deploy the application at the solution level you can expand the entry point to use dotnet test. Também é possível usar outro arquivo do Compose para executar testes na imagem de destino.You can also use another compose file that would run your tests in the image you are targeting. Ao utilizar outro arquivo do Compose para testes de integração que inclui microsserviços e bancos de dados em contêineres, você garante que dados relacionados sempre sejam redefinidos para seu estado original antes de executar os testes.By using another compose file for integration tests that includes your microservices and databases on containers, you can make sure that the related data is always reset to its original state before running the tests.

Se o Visual Studio estiver em execução, será possível aproveitar pontos de interrupção e exceções após o aplicativo do Compose entrar em funcionamento.Once the compose application is up and running, you can take advantage of breakpoints and exceptions if you are running Visual Studio. Outra opção é executar os testes de integração automaticamente no pipeline de CI no Azure DevOps Services ou em qualquer outro sistema CI/CD compatível com contêineres do Docker.Or you can run the integration tests automatically in your CI pipeline in Azure DevOps Services or any other CI/CD system that supports Docker containers.

Testando em eShopOnContainersTesting in eShopOnContainers

Os testes do aplicativo (eShopOnContainers) de referência foram recentemente restruturados e agora há quatro categorias:The reference application (eShopOnContainers) tests were recently restructured and now there are four categories:

  1. Unidade testa, os antigos testes de unidade regulares, contidos nos projetos {MicroserviceName}.UnitTestsUnit tests, just plain old regular unit tests, contained in the {MicroserviceName}.UnitTests projects

  2. Testes funcionais/integração de microsserviço, com casos de teste que envolvem a infraestrutura para cada microsserviço, mas isolado dos outros; estão contidos nos projetos {MicroserviceName}.FunctionalTests.Microservice functional/integration tests, with test cases involving the infrastructure for each microservice but isolated from the others and are contained in the {MicroserviceName}.FunctionalTests projects.

  3. Testes funcionais/de integração do aplicativo, que se concentram na integração de microserviços, com casos de teste que exercem vários microserviços.Application functional/integration tests, which focus on microservices integration, with test cases that exert several microservices. Esses testes estão localizados no projeto Application.FunctionalTests.These tests are located in project Application.FunctionalTests.

  4. Testes de carga, que se concentram nos tempos de resposta para cada microserviço.Load tests, which focus on response times for each microservice. Esses testes estão localizados no projeto LoadTest e precisam do Visual Studio 2017 Enterprise Edition.These tests are located in project LoadTest and need Visual Studio 2017 Enterprise Edition.

Os testes de unidade e de integração por microsserviço estão contidos em uma pasta de teste em cada microsserviço e o testes de carga e de aplicativo estão contidos na pasta teste da pasta da solução, conforme mostrado na Figura 6-25.Unit and integration test per microservice are contained in a test folder in each microservice and Application a Load tests are contained under the test folder in the solution folder, as shown in Figure 6-25.

Captura de tela do VS apontando alguns dos projetos de teste na solução.

Figura 6-25.Figure 6-25. Estrutura da pastas de teste em eShopOnContainersTest folder structure in eShopOnContainers

Testes funcionais/integração de aplicativos e microsserviços são executados no Visual Studio, usando o executor de testes regular, mas primeiro você precisa iniciar os serviços de infraestrutura necessários, por meio de um conjunto de arquivos docker-compose contidos na pasta de teste da solução:Microservice and Application functional/integration tests are run from Visual Studio, using the regular tests runner, but first you need to start the required infrastructure services, by means of a set of docker-compose files contained in the solution test folder:

docker-compose-test.ymldocker-compose-test.yml

version: '3.4'

services:
  redis.data:
    image: redis:alpine
  rabbitmq:
    image: rabbitmq:3-management-alpine
  sql.data:
    image: microsoft/mssql-server-linux:2017-latest
  nosql.data:
    image: mongo

docker-compose-test.override.ymldocker-compose-test.override.yml

version: '3.4'

services:
  redis.data:
    ports:
      - "6379:6379"
  rabbitmq:
    ports:
      - "15672:15672"
      - "5672:5672"
  sql.data:
    environment:
      - SA_PASSWORD=Pass@word
      - ACCEPT_EULA=Y
    ports:
      - "5433:1433"
  nosql.data:
    ports:
      - "27017:27017"

Portanto, para executar os testes funcionais/integração, primeiro você deve executar esse comando na pasta de teste da solução:So, to run the functional/integration tests you must first run this command, from the solution test folder:

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

Como você pode ver, esses arquivos docker-compose só iniciam os microsserviços Redis, RabbitMQ, SQL Server e MongoDB.As you can see, these docker-compose files only start the Redis, RabbitMQ, SQL Server and MongoDB microservices.

Recursos adicionaisAdditional resources