Introdução
Embora não exista um consenso sobre o escopo de um teste de integração, Martin Fowler define testes de integração restritos (Narrow), nos quais a integração entre os sistemas é testada usando substitutos (Mocks), e testes de integração amplos (Broad), que se comunicam com APIs reais.
Nesse post, explicarei como criar mocks para APIs HTTP em testes de integração restritos usando a biblioteca WireMock.Net.
O que “mockar”?
Vladimir Khorikov tem um conceito de dependências gerenciadas e não gerenciadas, que considero complementar ao conceito de Martin Fowler, para escolhermos o que deve e o que não deve ser “mockado”.
Segundo Vladimir, dependências gerenciadas são sistemas externos controlados por nós e acessados apenas pela nossa aplicação (por exemplo, um banco de dados). Por outro lado, dependências não gerenciadas são sistemas externos não controlados por nós ou que são acessados também por outras aplicações (como uma API de terceiros e um message broker).
Vladimir defende que devemos fazer nossos testes de integração consumindo dependências gerenciadas e “mockando” dependências não gerenciadas. Eu acredito que essa definição serve mais como um norte do que como uma regra. Por exemplo, em um cenário onde nosso sistema posta mensagens em um message broker para outro sistema ler, ou seja, o message broker é uma dependência não gerenciada, podemos testar a integração com o message broker para validar que a mensagem está sendo postada no formato correto (contrato definido). Isso pode ser valioso para validar que atualizações na biblioteca usada para se comunicar com o message broker não modificou o formato e o conteúdo das mensagens.
Por que usar mocks?
O intuito dos testes de integração restritos é testar se seus componentes, testados individualmente através de testes de unidade, funcionam corretamente de forma integrada entre si. Quando consumimos uma API, seguimos um protocolo e confiamos em um contrato de comunicação, isto é, que ela vai aceitar uma entrada X e retornar uma resposta Y.
Dessa forma, o funcionamento da API externa não está no escopo dos testes integrados.
Isso não exclui a necessidade de realizar testes funcionais; apenas diminui a quantidade necessária desses testes, que, em geral, são mais custosos de serem executados.
Restringindo os testes de integração apenas ao nosso sistema, temos alguns benefícios:
- Velocidade nos testes, pois retiramos a latência da rede;
- Não dependência de dados para teste em sistemas externos;
- Menor fragilidade dos testes, que podem quebrar em caso de instabilidade ou de dados que foram alterados nos sistemas externos, dificultando a interpretação dos resultados;
- Maior confiança nos resultados dos testes.
Usando WireMock.Net
Nesse exemplo, construí uma API que usa o serviço PokéAPI para buscar os dados de um Pokémon e retornar os dados para o cliente da nossa API.
Controller
O controller é simples e usa a biblioteca Refit para abstrair a chamada da API e, por simplicidade, retorna o mesmo objeto.
|
|
Teste de integração padrão
Começamos com um teste de integração padrão, usando a classe WebApplicationFactory do ASP.NET Core.
O teste cria uma instância da nossa aplicação e faz uma chamada ao endpoint /pokemoninfo
passando o parâmetro charmander
. A chamada ao endpoint faz nossa aplicação chamar a API PokéAPI.
💡 Você pode usar qualquer classe do projeto da API para instanciar o
WebApplicationFactory
nos seus testes. Se estiver usando statements na sua aplicação, você pode usar a classe do controller. Por exemplo,WebApplicationFactory<PokemonInfoController>
.
|
|
Criando um mock para o sistema externo PokéAPI
O WireMock.Net é uma biblioteca que possibilita a criação de mocks para APIs HTTP. Ele cria um servidor web no mesmo processo dos nossos testes e expõe uma URL para ser acessada pela nossa aplicação.
Usando o WireMock.Net e o WebApplicationFactory teremos o seguinte cenário:
Primeiro, instalo o pacote Nuget WireMock.Net
no projeto de testes.
Utilizando o Package Manager pelo Visual Studio
Install-Package WireMock.Net
Ou
Utilizando o .NET CLI pela linha de comando
dotnet add package WireMock.Net
Iniciando o servidor do WireMock.Net
Para iniciar o servidor do WireMock.Net, chamo o método Start
da classe WireMockServer
, que retorna um objeto com os dados do servidor.
|
|
Sobrescrevendo as configurações da nossa aplicação
Com o servidor iniciado, sobrescrevo o parâmetro PokeApiBaseUrl
, que contém a URL do PokéAPI, nas configurações da minha aplicação usando o método WithWebHostBuilder
do WebApplicationFactory
:
|
|
Criando o mock para o endpoint /pokemon
Em seguida, crio o mock para o endpoint /pokemon
recebendo o parâmetro charmander
.
No exemplo abaixo, estou usando o AutoFixture para gerar um objeto com valores aleatórios, que será retornado pelo mock.
ℹ️ Usar um objeto me possibilita comparar o retorno do meu sistema com esse objeto, mas também é possível configurar o retorno a partir de um arquivo, utilizando
WithBodyFromFile
.
Também configuro os Headers que serão retornados e o status HTTP.
|
|
Feito isso, minha aplicação instanciada dentro dos testes já estará consumindo o mock, em vez da API real.
Código completo do teste
|
|
Exemplo simulando um cenário de não sucesso
Pelo contrato da API, sabemos que ela retorna o status 404 (Not Found) quando recebe um pokémon inválido como parâmetro, então criei um mock que retorna esse status ao receber o parâmetro picapau
e valido que o retorno da minha aplicação está correto.
|
|
Fonte completo
https://github.com/dgenezini/PokemonInfoAPIWireMockTests