DEV Community

Alessandra Souza
Alessandra Souza

Posted on

Mockando Injeção de Dependência em Testes Integrados no ASP.NET 3.1

O que é um Teste de Integração?

O teste de integração é o teste entre diferentes módulos em um sistema, testando a integração entre as unidades. Muito útil para testar middlewares, mas além disso é possível testar uma requisição completa, evitando falhas como as citadas abaixo:

Erros de interpretação: ocorre quando uma funcionalidade está implementada diferente da especificação.
Chamadas incorretas: quando existe a chamada de unidades em locais errados.
Erros de interface: quando o padrão de integração entre as unidades foi implementado erroneamente, seja a ordem, tipo ou formato de parâmetros errados.

Para este tutorial, foram feitas algumas modificações na API que o próprio modelo ASP.NET Core Web Application cria ao inicializar um novo projeto.

 [ApiController]
public class WeatherForecastController : ControllerBase
{
    private readonly IWeatherService _weatherService;
    public WeatherForecastController(IWeatherService weatherService)
    {
        _weatherService = weatherService;
    }

    [HttpGet("/report")]
    public ContentResult GetWeatherForecastReport()
    {
        return new ContentResult()
        {
            Content = _weatherService.WriteWeatherForecastReport(),
            StatusCode = 200
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Criei uma camada de serviço e repositório para obter a previsão do tempo ao invés de deixar este código na controller.

Agora iremos partir para o teste de integração!

Criação de um Teste de Integração

Para criar um teste de integração é necessário criar um host e para isso utilizaremos o HostBuilder. Para saber mais sobre host e as mudanças do Asp.Net Core 3.1 para as versões anteriores, eu sugiro a leitura deste artigo. Link

var hostBuilder = new HostBuilder()
            .ConfigureWebHost(webHost =>
            {                      
                webHost.UseTestServer();
                webHost.UseEnvironment("Test");
                webHost.UseStartup<Startup>();
            });
Enter fullscreen mode Exit fullscreen mode

O UseTestServer indica que iremos utilizar o TestServer.

Nota: O UseTestServer está dentro do pacote Microsoft.AspNetCore.Mvc.Testing.

Em UseEnvironment indicamos qual ambiente iremos utilizar para realizar o teste.

Por último informamos qual classe Startup iremos utilizar.

Após construir o host, precisamos inicializá-lo, e para isso utilizamos o código abaixo:

var host = hostBuilder.StartAsync();

Agora é necessário criar uma instância para este servidor em memória que acabamos de criar.

var client = host.GetTestClient();

Por fim, fazemos a chamada do endpoint.

var response = client.GetAsync("/report");

Vamos ver agora como ficou o teste completo:

[Fact]
public void TestReport()
{
    //Arrange
    var hostBuilder = new HostBuilder()
            .ConfigureWebHost(webHost =>
            {                      
                webHost.UseTestServer();
                webHost.UseEnvironment("Test");
                webHost.UseStartup<Startup>();
            });

    var host = hostBuilder.Start();
    var client = host.GetTestClient();

    // Act
    var response =  client.GetAsync("/report");
    var responseString = response.Result.Content.ReadAsStringAsync();

    // Assert
    Assert.NotNull(responseString);
}
Enter fullscreen mode Exit fullscreen mode

Teste de Integração Trocando a Injeção de Dependência

Agora que você já sabe a estrutura básica de um teste de integração no Asp.Net Core 3.1, vamos aprender como trocar a injeção de dependência.

No cenário anterior, temos a controller com uma injeção de dependência para o serviço e o serviço dependia de uma interface do tipo IWeatherRepository.

public class WeatherRepository : IWeatherRepository
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecast GetWeatherForecast()
    {
        var rng = new Random();
        return  new WeatherForecast
        {
            Date = DateTime.Now,
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        };

    }
}
Enter fullscreen mode Exit fullscreen mode

O repositório simplesmente devolvia uma previsão do tempo randômica, mas e se quisermos trocar por um repositório fake que esteja no projeto de teste para sempre nos devolver uma mesma previsão do tempo?

Para isso será necessário adicionar uma configuração a mais no construtor do host.

Na construção do host, temos acesso ao método ConfigureTestServices. Caso ele seja adicionado depois do UseStartup, nós podemos acessar o IServiceCollection que foi gerado anteriormente para criar um provedor de serviços.

webHost.ConfigureTestServices(services =>
     {
         services.SwapTransient<IWeatherRepository, FakeWeatherRepository>();
     });
Enter fullscreen mode Exit fullscreen mode

Com o método abaixo, podemos buscar qual foi a implementação configurada na Startup para a interface, e realizar a troca pela implementação que quisermos.

public static void SwapTransient<TService, TImplementation>(this IServiceCollection services)
where TImplementation : class, TService
{
    if (services.Any(x => x.ServiceType == typeof(TService) && x.Lifetime == ServiceLifetime.Transient))
    {
        var serviceDescriptors = services.Where(x => x.ServiceType == typeof(TService) &&
        x.Lifetime == ServiceLifetime.Transient).ToList();
        foreach (var serviceDescriptor in serviceDescriptors)
        {
            services.Remove(serviceDescriptor);
        }
    }

    services.AddTransient(typeof(TService), typeof(TImplementation));
}
Enter fullscreen mode Exit fullscreen mode

O método de teste ficou assim:

[Fact]
public void TestReportWithFixForecast()
{
    //Arrange
    var hostBuilder = new HostBuilder()
         .ConfigureWebHost(webHost =>
         {
             webHost.UseTestServer();
             webHost.UseEnvironment("Test");
             webHost.UseStartup<Startup>();
             webHost.ConfigureTestServices(services =>
             {
                 services.SwapTransient<IWeatherRepository, FakeWeatherRepository>();
             });
         });

    var host = hostBuilder.Start();
    var client = host.GetTestClient();

    // Act
    var response = client.GetAsync("/report");
    var responseString = response.Result.Content.ReadAsStringAsync();

    // Assert
    Assert.Equal("Today's weather is Warm. With temperature 73", responseString.Result);
}
Enter fullscreen mode Exit fullscreen mode

Caso queira consultar o código utilizado neste artigo, é só acessar o link do repositório no GitHub.

Referências

Este artigo foi fortemente baseado em uma série de artigos do Adam Storr, indico a leitura caso tenha interesse em se aprofundar mais no assunto.

Caso queira saber mais sobre testes de integração indico a leitura desta tese.

Por fim não deixe de ler a documentação do ASP.NET Core.

Top comments (4)

Collapse
 
jessicanathany profile image
Jéssica Nathany

Ja vou utilizar como base nos meus estudos

Collapse
 
alsouza93 profile image
Alessandra Souza

Showw, muito feliz em ler isso =D

Collapse
 
sistemassouza profile image
Denis Souza

Artigo simples e prático. Show! Vou usar como referência

Collapse
 
alsouza93 profile image
Alessandra Souza

Opa, muito obrigada Denis!