Testes para API Rest com JUnit

Qual seria a melhor abordagem para criar testes unitários para uma API Rest?

Estou criando um sistema baseado em REST, utilizando Spring MVC e desejo criar uma estrutura de testes unitários, estou utilizando:

  • Spring MVC
  • Java 1.8
  • Mockito
  • JUnit
  • Spring Tests
  • Spring Data

Dei uma olhada em vários exemplos e a abordagem que mais se utiliza é baseada em criar um mock da classe Service e outro do controller.

Como vocês aplicariam estes testes?

1 curtida

Estranho testar a API REST, normalmente se unit test o model que a API REST é baseado.

@pfk66,estes links abaixo dão a dimensão da minha dúvida:

Testar spring rest api
Testes unitários para Spring MVC Rest API
Teste para Spring Rest WS

Pelo que pude entender, estes testes fazem mock do controller que representa o WS, ao passo que, também mockam (bela conjugação de verbo para um neologismo, não?) outras camadas.

Qual é a lógica de um teste nestes termos, quero dizer, a ideia do teste não seria validar o que entra e qual seu resultado? Fazer testes assim, mockando tudo, não pode ser considerado/interpretado como uma maneira de burlar “o sistema”?
É correto isso?
Afinal, se eu abro uma calculadora, digito 1 + 1, espero que o resultado seja 2.
Se eu faço login num site da web, espero que ele retorne um erro ou acesso às áreas restritas.
Entende minha dúvida? Até que ponto o uso de mock é interessante? Ele não ultrapassa o limite do teste de unidade, invadindo o teste integrado e, por consequência, dando uma falsa visão do funcionamento da API/sistema?

1 curtida

Sim, mockar é “burlar”, simular para reduzir custos de execução para quem não pode investir em execução de testes de verdade. Se precisa testar o serviço isolado por necessitar distribuir uma API para terceiro, faça uma requisição HTTP de verdade. Se só sua aplicação usa a requisição, faz o teste completo a partir da sua UI, se for via navegador pode usar o Selenium WebDriver. Se forem muitos testes é bom usar no servidor de testes o Selenium Grid. Mas parece que seu caso basta fazer a requisição HTTP isolada.

Você quer testar o que? Se o programa está chamando a API corretamente, ou se o serviço que disponibiliza a API está online e respondendo, ou outra coisa?

Se o endpoint ainda não está implementado, geralmente aplico o seguinte workflow:

  • Escrevo um cenário de teste de integração pro controller com request HTTP, que espera uma resposta X no response. Essa resposta é a garantia que meu contrato dessa parte da API vai funcionar.
  • Pra fazer esse primeiro cenário de integração passar, forço no controller o retorno da resposta X. Parece muito naive isso, mas no meu caso descreve o contrato mais básico dessa parte da API.
  • Com isso, vou criando testes unitários para os services necessários pra trazer aqueles dados. As vezes é apenas um, em outros casos são services que vão se compondo, depende do contexto.
  • Em algum momento em algum service/repository, preciso ir a algum BD buscar dados. Além do teste unitário, dependendo do caso escrevo também teste de integração pro service que traz dados da BD, geralmente alguma BD dummy, leve e embedded na suite de testes.
  • Quando os testes unitários e integração dos services estão passando, hora de voltar pro controller e adicionar cenários no teste de integração. Pra isso, crio um cenário com outro tipo de response esperado. Como ainda não havia mexido no controller, pois o response está “na mão”, esse segundo cenário vai falhar. Neste caso eu tiro aquele payload do controller e substituo pelo service que já está testado e implementado. Nesta altura, provavelmente todos os cenários de integração devem passar e o endpoint está implementado e testado.

Quando se trata de adicionar ou alterar elementos na resposta da API, o flow é parecido: cenário de integração do controller, depois teste unitário e/ou integração do service que adiciona/muda algum elemento na resposta. As vezes faço o caminho inverso, mudando primeiro os testes unitários de serviços e por último cenario de integração no controller.

Resumindo: geralmente faço teste de integração pro controller, e teste unitário + integração para services.

Me deparei com uma questão em um processo seletivo que dizia para “implementar e garantir boa cobertura de testes” e queria entender qual seria a boa cobertura de testes para uma API Rest.
Quando pesquisei, encontrei estes links que coloquei. Os mesmos me deixaram confuso, afinal, eu entendo que há fundamento em mocks quando se deseja testar a validação de regras de negócio, para validar mensagens esperadas e coisas assim.

Eu entendo que o caso seria este, o que eu não entendi foi, de acordo com os links, qual a razão de mockar os controllers. Se eu fosse o code reviewer, não aceitaria um teste assim.

1 curtida

Não sei, eu nunca fiz teste de mentirinha. Mas lembrando o que tinha falado no post anterior, um dos motivos de mockar pode ser a falta de investimento em infraestrutura para testes de verdade, de aceitação no alto nível. Se tenho que testar o web service pelo qual vou disponibilizar para terceiro, então simplesmente faço uma requisição HTTP para testa-lo. Para isso vai ser necessário um ambiente com sua aplicação de verdade rodando para atender os testes.

Pois bem pessoal, entendi que o que eu já imaginava está correto: testar a API Rest é um teste integrado, afinal, o endpoint não é a menor unidade testável da aplicação, o que justificaria criar um teste unitário.

O mock, ao meu ver, pode ser aplicado nos testes referentes à business/logic layer (onde a lógica e/ou as regras negociais estão implementadas).
O teste da API Rest deve ser encarado como teste integrado, em outras palavras, é o teste que valida aspectos mais abrangentes, como mensagens de retorno (sucesso, erro, alerta, etc) e outros aspectos inerentes.