Estratégia de teste para requisições

Muitas vezes queremos forçar um cenário para ver se nossa aplicação lida bem com ela. Um grande variantes são erros:

  • Serviço externo caiu e gerou erro 500

  • Serviço externo ficou lento e gerou um erro timeout com status 408

  • Serviço externo não quer que você fique requisitando o tempo todo e gerou um too much requests com status 428

  • Enviamos dados incorretos e o serviço não sabe lidar com eles gerando status 422

São vários cenários e precisamos cobrir a maioria, principalmente quando lidamos com algo que não está em nosso controle que é o caso de requisições externas.

Mas como forçar esses erros? Duvido muito que o serviço externo queira disponibilizar uma rota de erro ou quebrar seu app só para nossos testes. Isso também causaria problema de lentidao nos testes. Imagina, ter 10 testes de integração com o serviço externo e demorar 3 segundos para rodar cada um (é uma chamada externa, 3 segundos é bem rápido), teríamos um total de 30 segundos apenas para rodar esses testes. A cada teste novo, teríamos um acréscimo na lentidão até um ponto que se tornaria inviável.

Também temos outro problema. Muitas vezes o serviço é pago por utilização. Imagina rodar 1000 testes e ser cobrado por isso. Nem foi seu usuário que usou, foi você. Seria bem triste chegar no final do mês com uma conta alto de testes não?

Uma boa pratica é a utilização de ferramentas que fazem um mock de nossa requisição. Usamos a palavra mock como um adjetivo, isso é, Falso ou Simulado. Sendo assim, simulamos um chamada falsa. Visualmente, essa é a ideia

Imagem 01

Em testes iremos evitar requisitar o serviço diretamente, mas iremos usar como base o retorno dele. Essa resposta será adicionado em nossa massa de teste e ela é imutável. Isso quer dizer, uma vez que o serviço mude a resposta, ele não refletirá nesse dado criado em nosso projeto. Teremos que alterar eles na mão. Mesmo com esse contra tempo, o custo é baixo, uma vez que não trocamos com tanta facilidade a resposta. Mas fiquem sempre atentos a isso.

Um pouco de explicação

Primeiro de tudo, vamos entender como nosso ciclo de vida funciona.

Imagem 02
  1. O Client configura e mantem as funções relacionados ao serviço externo;

  2. Dentro dele a função all_hot_coffees/0 executa a requisição;

  3. O Tesla roda a chamada utilizando a função do método GET e aguarda a resposta do serviço;

  4. O serviço devolve a resposta fazendo o caminho de inverso;

  5. Recebemos a resposta e a tratamos como quisermos.

Entendido isso, vamos ver as formas de realizar esse desvio, para que não chegarmos a bater no serviço e termos uma resposta, como na imagem 01.

Estamos utilizando Tesla como cliente HTTP. Na própria ferramenta existe formas de criar esse mock. A ideia por trás é fazer o mock diretamente na função get do Tesla, deixando nosso fluxo assim

Imagem 03
  1. O Client configura e mantem as funções relacionados ao serviço externo;

  2. Dentro dele a função all_hot_coffees/0 executa a requisição;

  3. O Tesla entrega diretamente a resposta falsa para o método GET;

  4. Recebemos a resposta e a tratamos como quisermos.

Isso já resolve nosso problema. Não ficamos batendo todo tempo no serviço e podemos simular diversos cenários.

Porém, um ponto de atenção. Percebeu que tem um passo a menos quando o mock está ativo? Isso acontece porque não temos um requisição real quando mockamos com o Tesla. A requisição nunca é feito de verdade. Particularmente eu não gosto disso. Quando realizamos requisições, algumas coisas devem ser garantidas. Como formato de envio, resposta criado pela ferramentas entre o outros pontos, mas no Tesla.Mock criamos isso tudo na mão e a chance de algo incoerente acontecer não é pequena. Pode ser até a atualização da biblioteca ou utilização interna.

Eu prefiro trabalhar com requisições reais. Nessa hora você deve estar com vontade de enfiar esse livro no meu... Calma! Falei que prefiro que a requisição seja real, não que o dado seja real.

O fluxo que eu sempre desejo seria assim:

Imagem 04
  1. O Client configura e mantem as funções relacionados ao serviço externo;

  2. Dentro dele a função all_hot_coffees/0 executa a requisição;

  3. O Tesla roda a chamada utilizando a função do método GET e aguarda a resposta do serviço;

  4. O serviço devolve a resposta mockada fazendo o caminho de inverso;

  5. Recebemos a resposta e a tratamos como quisermos.

Você já deve ter notado que é basicamente o mesmo passo a passo da requisição real, mas, ao invés de bater no serviço, temos um serviço de teste. Gosto bastante desse modelo porque temos um real entendimento do que esta acontecendo. Uma requisição HTTP é feito para um "serviço" false que temos disponível e que responde igual a uma resposta HTTP. Isso é perfeito para garantir comportamento real x teste.

Para resolver isso, existe uma biblioteca chamada Bypass que gosto bastante. Utilizaremos ela.

Atualizado