Comece seus testes de API com Supertest

Sayoan Oliveira
Fretebras Tech
Published in
10 min readMar 23, 2022

Dando continuidade ao nosso post anterior, hoje iremos falar de como consumimos a estrutura das nossas API’s e as testamos através da execução dos testes automatizados utilizando Supertest, ferramenta escolhida na POC de ferramenta de testes automatizados de API dentro da Fretebras Tech.

Por possibilitar a comunicação entre mais de um sistema, os testes de APIs são importantes para garantir que a qualidade e a segurança do serviço esteja de acordo com o que foi planejado. O serviço de um garçom é uma excelente analogia à função de uma API:

Figura 1 — Estrutura de uma API. Fonte

O cliente faz uma requisição ao garçom (API), o garçom por sua vez vai na cozinha (banco de dados) e a cozinha retorna com os itens do pedido, o garçom (API) organiza o pedido e entrega para o cliente. O retorno do pedido é chamado de Response e nele contêm informações como body, header e status code. Com isso temos algumas situações:

  • Se o cliente fizer um pedido que existe na cozinha ele terá seu pedido atendido com sucesso (status code 2xx e response body).
  • Se o cliente faz um pedido que não é possível processar pela cozinha, podemos ter um erro pelo lado do cliente (status Code 4xx e response body), como pedir pra fazer uma feijoada com sorvete e o garçom avisa que esse pedido não existe.
  • Se o cliente faz um pedido que existe na cozinha mas na cozinha teve um problema como falta de gás, teremos um erro (status code 5xx e response body) do lado do servidor (cozinha).

Com isso podemos entender que além de fazermos um pedido precisamos nos atentar às respostas dos pedidos que são feitos, para isso temos a escala dos códigos de status http ( HTTP status codes).

Figura 2 — HTTP Status Codes. Fonte

Estrutura de uma requisição (Request)

  • API ou serviço
    Serviço que provê rotas.
  • Método (verbo)
    Método implementado para rota, uma rota pode ter vários verbos como GET, POST, DELETE, PATH, PUT…
  • Headers (cabeçalhos)
    Os cabeçalhos HTTP permitem que o cliente e o servidor passem informações adicionais com a solicitação ou a resposta HTTP.
  • Endpoint (rota)
    Recurso que a API disponibiliza para o cliente.
  • Request Body
    Algumas rotas precisam de um Request Body, podendo ser uma estrutura JSON.

Estrutura de uma resposta (Response)

  • Response Body (corpo da resposta)
    Json contendo dados de retorno da solicitação feita.
    Ex: Uma lista de motoristas.
  • Cookies
    Informações do usuário.
  • Headers
    Os cabeçalhos HTTP permitem que o cliente e o servidor passem informações adicionais com a solicitação ou a resposta HTTP.
  • Status Code
    Resposta Status HTTP dada pelo servidor, esse status é a peça fundamental para entendermos como o Cliente e Servidor se comunicaram.
  • Response Time
    Tempo de resposta.

Validação de Status Code (HTTP Status)

Durante os testes de API é necessário se atentar às variedades dos Status Codes pois nele teremos muitas informações importantes sobre como nossa API processou o pedido.

  • Valide se o pedido feito foi processado com sucesso.
    Ex: Em uma endpoint de cadastro de pessoas, ao enviar a requisição (request) com todos os dados obrigatórios preenchidos, neste caso esperamos sucesso da requisição (request) e como resposta (response) esperamos um código de status com variedade 2XX:
    201 — Solicitação atendida
    200 — OK
  • Valide a se o usuário tem permissão pra realizar uma determinada ação.
    Ex: Simule um acesso à uma API com o cabeçalho de autenticação HTTP com credenciais inválidas. E o servidor deve retornar um código com variedade 4XX.
    401 — Unauthorized : Ausência de token ou token gerado com escopo inválido.
    403 — Forbidden : Token gerado é válido porém o cliente não tem autorização para acessar o recurso.
  • Verifique vários códigos de erro, para garantir a validação de uma resposta. Alguns dos códigos de validação incluem:
    404 (não encontrado): Requisição não conseguiu encontrar nenhum dado, muito comum quando buscamos uma informação inexistente.
  • Verifique se as regras de negócio estão na camada de serviço, tente fazer um cadastro com um dado obrigatório faltante. A API deverá retornar um status code (400, 409 ou 422, isso vai depender de empresa pra empresa mas é necessário manter a variedade 4XX) alertando que a requisição não foi atendida por não conter os dados obrigatórios.
  • Valide se os erros por parte do servidor estão devidamente tratados.
    503 — Serviço indisponível
    504 — Gateway Timeout

Os seguintes pontos são levados em consideração no teste de API:

  • Status code ser coerente (Exemplo: Caso eu tente obter um token com uma senha inválida, o status code precisa ser 401);
  • Tempo de resposta não ser muito elevado;
  • O response body precisa estar de acordo com o que é esperado da funcionalidade;
  • Em testes de requisições com métodos POST, PUT ou DELETE, realizar a consistência posteriormente com um endpoint GET para verificar se a ação foi consistida no banco de dados. Como cadastrar (POST) um usuário e depois listá-lo (GET).

O que é um teste automatizado?

O teste automatizado é um processo que valida se o software está funcionando adequadamente e atendendo aos requisitos antes de ser lançado para o usuário (disponibilizado em produção). Este método de teste de software usa sequências de script que são executadas por ferramentas de teste automatizado. Ferramentas de teste automatizado executam uma bateria de testes sem intervenção humana e com isso existe uma grande geração de valor para a equipe, assim relatando resultados e comparando os resultados com testes anteriores. Neste momento buscamos entender se o que foi executado está conforme esperávamos. Estes testes automatizados podem ser simulando um usuário final (testes Web) ou até pequenas partes do sistema como APIs. Não iremos falar neste artigo sobre pirâmide de testes, sugiro a leitura.

Supertest, um framework de testes automatizados de API, tem como principal foco testar pequenas partes do projeto de forma automatizada. Automações de API garantem que a regra de negócio e o contrato (leia mais sobre contrato de uma API) da API estão em conformidade com o esperado, assegurando que quando acoplarmos o Backend (API) com o Frontend (Web ou Mobile) as regras já estarão testadas “de baixo para cima” (visão da pirâmide de testes). A geração de valor é imediata, podemos testar desde de simples requisições como buscar nomes que não existem na base e assegurar que irá retornar aquele status code e response body até questões de segurança como token no cabeçalho das nossas rotas.

Escrevendo os testes automatizados

“Todo teste automatizado um dia foi um teste manual”.
Autor desconhecido.

Munido disto o que precisamos é escrever nossos casos de testes e categorizá-los, uma vez que nem tudo que foi levantado precisa ser automatizado. Para que irei fazer automação de partes do sistema que não geram valor? Sempre é preciso categorização pensando no valor para o negócio. Exemplos de Casos de teste e sua respectiva categorização, temos um exemplo de sucesso (nome existe na base de dados) e de nome inexistente.

Utilizaremos então o ambiente de testes chamado ServeRest, o ServeRest é uma API REST que simula uma loja virtual com intuito de servir de material de estudos de testes de API. Dentro desta loja virtual temos listagem (GET), cadastro (POST), exclusão (DELETE) e atualização (PUT) de usuários, produtos e carrinhos de compras. Para este artigo utilizaremos a funcionalidade de Usuários dentro do ServeRest:

Funcionalidade: Usuários

Caso de teste: Buscar usuário pelo nome
Prioridade: Alta
Request: dominio.com/usuarios?nome=NOMEBUSCADO
Response:
http status: 200
response body:

Figura 3: Response Body esperado. Fonte do Autor.

Caso de teste: Buscar usuário inexistente pelo nome
Prioridade: Alta
Request: dominio.com/usuarios?nome=11223aaa122
Response:
http status: 200
response body:

Figura 4: Response Body esperado. Fonte do Autor.

Já definimos alguns casos de testes da API Serverest, agora iremos entender como a estrutura do projeto com Supertest organizará estes casos de testes.

A estrutura do projeto com Supertest

A estrutura proposta visa ter reuso pensando na responsabilidade única das classes de testes e da classe de rotas. Buscamos isolar as partes pois podemos ter vários casos de testes (test.js) usando a mesma rota (route.js) e quando precisar de qualquer manutenção em uma rota não é necessário alterar inúmeras classes, somente o módulo onde está a rota.

Figura 5: Proposta de arquitetura de testes. Fonte do Autor.

Motivações para ter uma camada de rotas:
- Reuso da rota em outros testes;
- Refatoração somente em um local;
- Responsabilidade única das classes;
- Possibilidade de uma rota receber inúmeros parâmetros, alterando assim somente o dado de entrada.

route.js (figura 6): Camada responsável por estruturar o reuso da rota, neste caso é um GET do tipo Query String, onde recebemos um parâmetro chamado nome. Poderíamos receber nenhum ou mais de um parâmetro, mas neste exemplo mostraremos somente com o nome.
A função report será a nossa camada de report, conforme a estrutura mostrada ela está na nossa utils.js.

Figura 6: Classe route.js. Fonte do Autor.

usuario.test.js (figura 7): Classe responsável por estruturar toda camada de testes, nesta camada colocaremos o título do teste (it), estruturamos como iremos interagir (neste caso utilizaremos o padrão AAA) e o que iremos validar (Assertions/Expect).

Falando de AAA conforme Figura 7:
- Arrange: Preparação de dados, neste caso preparamos o nome (Bert).

- Act: Ação desejada, neste caso estamos chamando a rota getUsuario.

- Assert: Validação, como estamos falando de API precisamos nos atentar a tudo que foi retornado no nosso Response tais como:
Response Body: Corpo da resposta com os dados.
HTTP Status code: Código de status que nos mostra como foi o comportamento da API, na computação temos alguns intervalos de valores tais como:
- 2xx, 3xx, 4xx e 5xx conforme mostrado nos tópicos anteriores.

O exemplo abaixo mostra dois tipos de validações possíveis do response.body, ambas com o mesmo resultado, a primeira com uma abordagem individual dos atributos e a segunda com a validação do response body de forma completa.

Figura 7: Classe usuario.test.js. Fonte do Autor.

Executando os testes automatizados

Ferramenta escolhida, sistema alvo selecionado e código escrito. Chegou a hora de executar essa bateria de testes automatizados de API através da ferramenta Supertest (para mais detalhes acessar a documentação oficial). Podemos executar localmente através do comando configurado no projeto ou executar em um pipeline de testes de automatizados.

Criamos o script ‘test-html’ para execução dos testes através do mocha e também gerar um relatório html, essencial para o entendimento do resultado do teste independente de sucesso/falha. Colocamos um timeout de 40 segundos caso tenhamos qualquer tipo de problema durante a execução, assim asseguramos que a execução terá uma tolerância de tempo de resposta.

“test-html”: “mocha — timeout 40000 — reporter mochawesome”

Agora é só executar o comando ‘npm run test-html’ para os testes de API serem executados e o relatório html ser gerado.

Figura 8: Console com o script executado. Fonte do Autor.

Percebam que só tínhamos um teste a ser executado ‘Get — Buscar Usuarios Nome’ e o mesmo deu sucesso na busca e validação (assertions citadas acima). Com o relatório gerado é possível expandir e verificar tudo que foi enviado (request) e o que foi recebido (response) com isso ajudando a entender em caso de falha.

Figura 9: Relatório HTML sem expandir as informações. Fonte do Autor.

E em caso de falha? Como podemos ver os dados no relatório?
Vamos supor que este usuário buscado troque seu email e ao buscarmos e fazermos a validação o mesmo deve gerar erro, então fui lá no ambiente de testes Serverest e alterei manualmente o dado de email para novoemail@gmail.com deste usuário através da rota de PUT/usuarios.

O teste falhará se executarmos conforme a figura abaixo, esperávamos o e-mail Skyla41@gmail.com e encontramos o novo email, com isso o relatório nos ajuda a entender o retorno da requisição. Não existe erro na aplicação mas sim uma mudança de dado e com isso sua automação precisa ser atualizada com o novo e-mail, o melhor caminho neste caso é preparar uma massa de testes antes de executar sua bateria regressiva.

Figura 10: Validação do antigo email. Fonte do Autor.
Figura 11: Dados da execução do Script. Fonte do Autor.
Figura 12: Relatório HTML da falha. Fonte do Autor.

Com isso foi possível ter um arquitetura base para os testes automatizados de API que estamos rodando aqui dentro da Fretebras Tech através de Pipeline no GitlabCI, já vimos e já aplicamos muitas evoluções da arquitetura como:
- Uso de JOI para validação do Schema;
- Integração com banco de dados para ter uma massa de testes bem preparada.
- Uso a abordagem de Data-Driven-Testing para maior variedade de dados de entrada.
- Uso da camada de Fixture para request body (gerados randomicamente com Faker/Faker-br) e response body.

Em breve mais tópicos aprofundando nas estratégias de testes de API com Supertest, pipelines e JOI.


Revisão: Vanessa Marques e Renata Abreu

--

--