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
};
}
}
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>();
});
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);
}
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)]
};
}
}
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>();
});
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));
}
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);
}
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)
Ja vou utilizar como base nos meus estudos
Showw, muito feliz em ler isso =D
Artigo simples e prático. Show! Vou usar como referência
Opa, muito obrigada Denis!