Testando Microservices com RestAssured
Testando Microservices com RestAssured
Elias Nogueira
Buy on Leanpub

Sumário

Prefácio

!!!Explicar sobre porque é importante a automação !!!

Por que este livro

Eu pensei neste livro como um guia que você pode consultar e utilizar a qualquer momento.
Você verá que, em algumas seções, haverá tópicos “Faça você mesmo” como um exercício. Ao invés de criar um curso com vídeos, exercícios e explicações eu preferi escrever um livro que você possa usar enquanto você estiver indo ou voltando do trabalho, em casa, em um café ou em qualquer outro lugar possibilitando que você visualize todo o conteúdo offline.

Este é um livro básico sobre testes e automação para uma API REST. Existem outros excelentes livros e cursos pela internet que você pode aprender sobre o mesmo tópico, logo não deixe de obter mais conhecimento depois de ler este livro.

Muito obrigado por você ter baixado o livro!

Para quem é este livro

Como ler este livro?

Embora este livro esteja em uma ordem cronológica para te ensinar desde a como baixar a aplicação de exemplo, criar a aplicação de teste, entender a documentação da aplicação de exemplo e iniciar na desenvolvimento dos testes utilizando Postman e RestAssured, você pode iniciá-lo em qualquer ordem.

Se você já tem uma noção sobre as ferramentas ou tem experiência com elas, este livro pode te servir como um guia rápido para relembrar alguns conceitos básicos focados em automação de teste para uma API REST.

Para reter a sua atenção…

Espero que você veja neste livro uma oportunidade de expandir ainda mais os seus conhecimentos em teste de software. Boa leitura!

Sobre o autor

Elias Nogueira é um Lead Software Engineer in Test, brasileiro mas atualmente vivendo nos Paises Baixos (Netherrlands). Ele publica posts sobre Qualidade e Testes de Software desde 2007 e ministra palestras sobre o mesmo tema desde 2008.

Contribui para diversas iniciativas, como a criação do primeiro grupo de usuários em teste de software do Brasil, o GUTS-RS, entre outros grupos de usuários.

É professor universitário convidado em algumas instituições de ensino e já organizou diversos eventos relacionados ao tema, sendo o mais conhecido a Trilha Testes e Trilha DevTest do The Developers Conference.

Você pode encontrar todas as atividades como palestras ministradas, palestras que ele ministrará, entre outras iniciativas na sua página de palestrante público.

Você também pode se conectar a ele através do Twitter, Blog e Linkedin.

O básico

Ferramentas necessárias

Para poder executar a aplicação alvo dos testes e também poder criar e executar os scripts de teste você deve ter as seguintes ferramentas instaladas:

Baixar as aplicações necessárias

Para que possamos executar todos os exercícios devemos baixar o projeto de teste e o backend.
O projeto de backend possui a aplicação alvo dos testes que iremos executar a todo momento.
O projeto de teste possui todas as bibliotecas necessárias configuradas e prontas para uso, além do esqueleto inicial do projeto.

Efetuar o clone do projeto

Projeto Backend

  1. Abra o Prompt de Comando (Windows) ou o Terminal (Mac ou Linux)
  2. Navegue até um diretório de sua escolha
  3. Execute o seguinte comando git clone https://github.com/eliasnogueira/credito-api.git
  4. Navegue, via Prompt de Comando ou Terminal, até o diretório do projeto cd credito-api
  5. Execute o comando mvn compile e aguarde até o término do build

Projeto de Testes

  1. Abra o Prompt de Comando (Windows) ou o Terminal (Mac ou Linux)
  2. Navegue até um diretório de sua escolha
  3. Execute o seguinte comando git clone https://github.com/eliasnogueira/projeto-teste-api-livro-v1.git
  4. Navegue, via Prompt de Comando ou Terminal, até o diretório do projeto cd projeto-teste-api-livro-v1
  5. Execute o comando mvn compile e aguarde até o término do build
  6. Abra a sua IDE de Desenvolvimento
  7. Nela, selecione a opção de importação de projetos para o tipo Maven apontando para a pasta projeto-teste-api-livro-v1
  8. Aguarde sua IDE efetuar todas as configurações necessárias

Observações

Se você tentar efetuar o clone do projeto e obter a mensagem abaixo, efetue os passos a seguir.

Mensagem

SSL certificate problem: self signed certificate in certificate chain

Como solucionar

Execute o comando abaixo:

git config --global http.sslVerify false

Sobre a aplicação de exemplo

A nossa API foi desenvolvida para dar suporte a uma aplicação web de simulação de empréstimo.
O ponto central aqui é explicar como seria uma página web para esta aplicação.

Por favor, não se apegue a detalhes nas imagens, mas sim as próximas explicações. Elas servem apenas para dar a você uma ideia de como seria a aplicação.

Design da aplicação

Página inicial

Nesta página é necessário informar um CPF para efetuar uma simulação de empréstimo.
Se o CPF possuir alguma restrição não será possível efetuar uma simulação, onde é apresentado uma mensagem ao usuário.

Página inicial da aplicação

Página de simulação

Se o usuário informou um CPF que não possui restrição ele é redirecionado para a página de simulação de crédito.
Aqui ele informa seus dados, bem como o valor que será tomado como empréstimo, parcelas e se ele deseja ter um seguro.

Há validações (regras de negócio) em algumas dos campos.

Página de cadastro

Página de sucesso

Uma vez finalizada a simulação o usuário visualiza uma tela de sucesso.

Página de sucesso

Página de visualização de simulações

Nesta página o avaliador visualizará a simulação de crédito, podendo efetuar algumas operações como pesquisar, atualizar e remover.

Página contendo a lista de simulações

Regras de Negócio da aplicação

Diversas aplicações possuem regras de negócio, e com esta não é diferente. Elas foram inseridas para que você possa estar o mais próximo possível da automação de uma aplicação real.
As regras de negócio estão escritas usando a abordagem de especificação por exemplo.

Consulta de CPF

Funcionalidade: Simulação de Crédito

Como uma pessoa que precisa de um empréstimo
Eu desejo efetuar uma simulação de crédito
Para que eu possa pagar as minhas dívidas

Cenário: Não possuo restrições
   Dado que eu não possuo nenhum tipo de restrição no meu CPF
   Quando eu efetuar uma solicitação
   Então eu visualizo que minha solicitação está em análise

Cenário: Simulador possui uma restrição
   Dado que eu possuo uma restrição no meu CPF
   Quando eu efetuar uma solicitação
   Então eu não consigo efetuar a solicitação de empréstimo
   
   Exemplos:
   | CPF para teste de restrição | Tipo de Restrição |
   |-----------------------------|-------------------|
   | 97093236014                 | Bloqueio Judicial |
   | 60094146012                 | Cartão de Crédito |
   | 84809766080                 | Bancária          |
   | 62648716050                 | SPC               |
   | 26276298085                 | SPC               |
   | 01317496094                 | Cartão de Crédito |
   | 55856777050                 | Bancária          |
   | 19626829001                 | Bancária          |
   | 24094592008                 | Bancária          |
   | 58063164083                 | Bancária          |

   
Cenário: Simulador de empréstimo não preenche informações necessárias
   Dado que eu não possuo nenhum tipo de restrição no meu CPF
   Quando eu deixo de informar algo importante
   Então eu não consigo efetuar a solicitação de empréstimo
   
   Exemplos:
   | Campo não informado    | Mensagem                       |
   |------------------------|--------------------------------|
   | Nome                   | Nome não pode ser vazio        |
   | E-mail                 | CPF não pode ser vazio         |
   | Valor solicitado.      | E-mail não deve ser vazio      |
   | Quantidade de parcelas | Parcelas não pode ser vazio    |
   

Cenário: Simulador de empréstimo solicita um valor menor ou maior do que o permitido
   Dado que eu não possuo nenhum tipo de restrição no meu CPF
   Quando eu informo um valor maior ou menor que o permitido
   Então eu não consigo efetuar a solicitação de empréstimo

   Exemplos:
   | Valor    | Mensagem                                  |
   |----------|-------------------------------------------|
   | R$ 900   | valor deve ser igual ou maior a R$ 1.000  | 
   | R$ 50.000| Valor deve ser menor ou igual a R$ 40.000 |
   
   
Cenário: Simulador de empréstimo informa uma parcela menor ou maior do que o permiti\
do
   Dado que eu não possuo nenhum tipo de restrição no meu CPF
   Quando eu informo uma parcela maior ou menor que o permitido
   Então eu não consigo efetuar a solicitação de empréstimo

   Exemplos:
   | Parcela | Mensagem                                  |
   |---------|-------------------------------------------|
   | 1       | Parcelas deve ser igual ou maior que 2    | 
   | 60      | Parcelas deve ser menor ou igual a 48     |   

Fora do escopo

Os seguintes itens estão fora do escopo para restringir um pouco nosso escopo de teste e dar mais foco na lógica de testes para API:

  • CPF válido
  • CPF formatado ###.###.###-##

Resumo

Neste capítulo você aprendeu a como instalar as ferramentas necessárias para iniciar os testes e automação de uma API.
Em seguida você aprendeu como seria a aplicação web desta API para te dar uma ideia de como ela seria.
Por fim você aprendeu as regras de negócio da aplicação.

Uma dica: sempre revisita as regras de negócio. Você as utilizará para desenvolver alguns testes na API.

Explicações gerais

Este capítulo tem o intuito de te contextualizar sobre as terminologias utilizadas, uma teoria básica e alguns itens comuns como status code, tipo de respostas, etc…

Este livro não tem o intuito de te ensinar uma teoria básica sobre o assunto REST APIs. Recomendo que você procure algum conteúdo teórico antes de iniciar o próximo capítulo do livro.

Terminologia utilizados

Durante a leitura deste livro você pode se deparar com as terminologias abaixo:

Terminologia Descrição
Recursos É o nome dado a abstração da API
endpoint Indica acesso a um recurso, geralmente uma URL
Métodos Indica as interações permitidas (GET, POST, PUT, etc…)
Parâmetros São opções que possamos passar ao endpoint para influenciar a resposta
Path parameter São parâmetros que aparecem no endpoint entre chaves. Indica que parâmetro é obrigatório
Query Parameter São parâmetros de pesquisa opcionais que aparecem no endpoint com ?atributo=valor
Request É a forma enviar informações a fim de obter um resultado. É composto pelo endpoint, método e parâmetros se necessário
Response É a informação retornada após um request. Pode ser apenas o status code ou pode conter uma response body
Response body São dados retornados pelo request em um formato pré-determinado, geralmente JSON ou XML
Status code São códigos numéricos, pertencentes a especificação HTTP, para identificar comportamentos

Status Code

São um padrão de códigos utilizados para entender o resultado de uma requisição. Existe uma padronização e uma divisão em cinco (5) categorias:

Categoria Descrição
1xx: Informacional Comunica informações de transferência a nível de protocolo
2xx: Sucesso Indica que uma requisição do cliente foi aceita com sucesso
3xx: Redirecionamento Indica que o cliente precisa tomar alguma ação adicional para completar a requisição
4xx: Erro no Cliente Esta categoria indica os possíveis problemas do lado do cliente
5xx: Erro no Servidor Esta categoria indica problemas do lado do servidor

Status code utilizados na aplicação

Os seguintes status code são utilizados na aplicação de backend que utilizaremos para os testes:

Status code Descrição Onde é utilizado
200 Quando obtemos sucesso na ação Nos verbos GET e PUT
201 Quando um recurso é criado com sucesso No verbo POST
204 Quando um recurso não retorna dados, mas a ação foi correta No verbo DELETE
404 Quando um recurso não é encontrado Nos verbos GET, PUT e DELETE
409 Quando há um conflito Nos verbos POST e PUT
422 Quando há falta de informações No verbo POST

Documentação de uma API REST

Uma API REST sempre possui uma documentação sobre o seu funcionamento. Normalmente a documentação apresenta como efetuar as requisições com os caminhos (paths) necessários, parâmetros e tipos de retorno. A documentação do projeto de backend foi desenvolvida utilizando Swagger.

Como acessar a documentação da API

Você deve estar com a aplicação iniciada para acessar a documentação.
Ela é disponibilizada através da url + porta + /swagger-ui.html.

Efetue o acesso no endereço http://localhost:8088/swagger-ui/index.html

Explicação da documentação

A partir de agora a explicação sobre cada API será feita associando um número dentro da imagem com uma lista numerada, assim quando você ler sobre um item da lista pode já fazer a associação direta com a imagem.

Na página inicial da documentação você visualizará dois itens:

  • Restrições
  • Simulações

Eles são chamados de controllers e cada um é responsável por efetuar uma ou mais ações de API para este contexto.

1. Os controllers são listados contendo o seu nome e descrição

Controllers Restrições e Simulações

Restrição

Este controller possui apenas duas chamadas do tipo GET. O intuito é efetuar uma consulta para saber se o CPF informado possui ou não uma restrição.

Note que há sempre um padrão em toda documentação de cada controller:

  1. Método HTTP
  2. URL da requisição
  3. Descrição da requisição
Controllers de Restrições e Simulações

Quando clicamos sobre a chamada (linha que possui o método HTTP + URL + descrição) podemos visualizar o detalhe da requisição e saber informações importantes como:

  1. Parâmetros
  2. Respostas
Controller da API de Restrição

Parâmetros

Os parâmetros podem ser ou não informações obrigatórias. Há dois tipos de parâmetros:

  • path parameter: onde inserimos o valor diretamente na URL
  • query parameter: onde inserimos o atributo e o valor diretamente na URL

No exemplo das Restrições temos um path parameter. Este tipo de parâmetros é representado pelo nome do parâmetro dentro de chaves. Exemplo: {cpf}.

A sessão Parameters apresenta quais são os parâmetros existentes, sendo composto por:

  1. nome do atributo
  2. tipo de dados + tipo do parâmetro
  3. obrigatoriedade
  4. descrição
Exemplo de parâmetros da API de Restrições

Respostas

As respostas não muito importantes no contexto de desenvolvimento e testes. Elas presentam comportamentos frente a fluxos principais (esperados), fluxos de exceção e regras de negócio.

Devemos prestar atenção em dois itens:

  • Code: Status code do retorno da requisição
  • Example Value: é o retorno esperado (Response Body)

No exemplo abaixo vemos:

  1. O Code (status code) como 200 para uma pessoa que possui restrição
  2. O exemplo de retorno (Response Body) como
{
  "mensagem": "O CPF 999999999 não foi encontrado"
}

* Logo, quando efetuarmos uma requisição para um CPF que possui uma restrição, receberemos estas duas informações.

  1. O Code como 404 para uma pessoa que não possui restrição e sem retorno (sem Response Body)
Controller da API de Simulações

Observação

Há um outro método GET na API de Restrição que será explicado em outro capítulo

Comumente referenciamos estas requisições pelo verbo seguido da URL que, neste caso, apresenta apenas o contexto que deve ser alcançado. Em outras palavras: na documentação da API o nome ou IP do servidor não é apresentado.

Exemplo:

GET /api/v1/restricoes/{cpf}

A leitura desta linha pode ser feita da seguinte forma: uma requisição do tipo GET para a API de Restrições, onde devemos informar o cpf como parâmetro.

A partir de agora a explicação para os próximos endpoints serão feitos desta maneira.

Simulações

A simulação é um CRUD (Create, Restore, Update e Delete) onde podemos inserir uma simulação de crédito. Note que na simulação existem diferentes requisições, uma para cada ação de CRUD.

Exemplo de retorno da API de Simulações

GET /api/v1/simulacoes

Esta requisição retorna todas as simulações existentes. Note que, o Example Value (referência número 2 na imagem) inicia o exemplo com colchetes. Isso quer dizer que o retorno é um array de elementos (simulações). Quando existir mais de uma simulação cadastrada ela será separada por vírgula após o fechamento das chaves. Exemplo:

Pesquisa de uma simulação pelo CPF

Você pode notar também que há um parâmetro (referência 1 na imagem acima) não obrigatório, do tipo query com o nome nome.
Falaremos sobre query parameters (parâmetros de consulta) mais tarde em um exercício.

GET /api/v1/simulacoes/{cpf}

Esta requisição retorna uma simulação existente dado um CPF, logo podemos ver que o CPF é obrigatório. Veja o item 1 da imagem.

Note que, quando a simulação é encontrada o status code é 200 e o retorno (response body) é um objeto de simulação. Veja item 2 da imagem.

Se a simulação para o CPF desejado não existir, o status code deverá ser 404 e nada será retornado no response body. Veja o item 3 da imagem.

Parâmetro não obrigatório e retorno como array

POST /api/v1/simulacoes

Esta requisição cria um recurso (neste caso uma simulação).

Note o seguinte na sessão Parameters:

  1. O parâmetro esperado é uma simulacao e o tipo é um body.
  2. Quando isso acontecer você deve enviar os mesmos atributos contidos no Exemple Value
  3. O tipo de dados deve ser igual ao item contido no campo Parameter content type. Nesta caso um application/json, ou seja, enviaremos um objeto JSON
Parâmetro necessário para o POST em Simulações

Note também que há diferentes retornos na seção Responses.

Quando a simulação for criada com sucesso notamos que:

  1. O status code retornado deve ser 201
  2. Não há respose body, logo não precisamos validá-lo
  3. É retornado o atributo Location no Header

O atributo Location provê informações sobre a localização de um recurso criado recentemente.
No nosso contexto de API esta informação é a URL completa do recurso que podemos localizar.

Exemplo:

Digamos que criamos um recurso com o CPF 12345678901.
O Location retornado será http://localhost:8088/api/v1/simulacoes/12345678901, exatamente a URL que podemos utilizar para consulta-lo através de uma requisição GET.

Retorno de sucesso

Existem outros dois retornos na seção Responses.

  1. Recebemos o status code 409 quando o CPF do objeto que estamos enviando já existe
  2. Recebemos o status code 422 quando existe a falta de alguma informação ou alguma regra de negócio é violada
Retornos de conflito e falta de informações

O item 1, status code 409, não possui retorno.
Já o item 2 possui o response body como um array e, dentro dele, um atributo erros. Este atributo possui uma série de propriedades e valores que é composto pelo nome do atributo que falta informação ou viola alguma regra de negócio.

Exemplo:

{
    "erros": {
        "parcelas": "Parcelas deve ser menor ou igual a 48",
        "valor": "Valor deve ser menor ou igual a R$ 40.000",
        "email": "must be a well-formed email address"
    }
}

PUT /api/v1/simulacoes/{cpf}

Esta requisição atualiza algum atributo (dado) de uma simulação existente. A chave de pesquisa da simulação existente é o CPF.

Você pode alterar todos os atributos ou somente os que você escolher.

  1. Devemos informar o cpf como chave de pesquisa como um path parameter
  2. Devemos informar o objeto simulacao
  3. Este objeto pode ser os atributos descritos no Example Value
  4. O tipo de conteúdo deve ser application/json
Parâmetros necessário para o PUT

A documentação do PUT apresenta três possíveis retornos:

  1. Quando a alteração for executada com sucesso o status code será 200
  2. O response body da simulação alterada com sucesso traz todos os atributos da simulação mesmo se não tiverem sido alterados
  3. Quando a alteração para a simulação com CPF desejado não existir, o status code será 404
  4. Se enviarmos uma simulação com um CPF alterado, e este já existir, o retorno será o status code 409
Retorno do PUT

DELETE /api/v1/simulacoes/{cpf}

Esta requisição remove uma simulação existente. A chave de pesquisa da simulação existente é o CPF.

  1. Para remover a simulação devemos informar o cpf como parâmetro no path (path parameter)
Parâmetros necessários para o DELETE

O DELETE tem dois possíveis retornos:

  1. O status code 204 quando a simulação foi removida com sucesso
  2. O status code 404 quando uma simulação com o CPF informado não foi encontrado
Retorno do DELETE

Testes Manuais para uma API REST

Em qualquer ação de automatizar uma funcionalidade é imprescindível que tenhamos executado a ação/funcionalidade ao menos uma vez de forma manual, a fim de entender o que deve ser automatizado e garantir que aquele fluxo não possua bug.

Para um API REST existem diversas formas que podem ser aplicadas o teste manual, sendo duas:

  • via cURL
  • via Postman

Via cURL

É uma ferramenta via linha de comando para transferir dados entre URLs, suportando diversos protocolos. Ele pode ser utilizado para testes manuais em uma API REST para ações rápidas em uma API, sem grandes necessidades de verificações.

Um exemplo é a rapidez e facilidade que temos para listar dados de um endpoint. Poderíamos executar o seguinte comando para saber se um CPF possui uma restrição financeira:

curl -v http://localhost:8089/api/v1/restricoes/12345678901

Ele é nativo em sistemas baseados em Unix (Linux e Mac). No Windows requer instalação. Você pode saber mais em https://curl.haxx.se

Via Postman (recomendado)

Há diversas ferramentas via interface gráfica para executar testes manuais para uma API REST. Uma das mais utilizadas no mercado é o Postman.

Com ele, além de ter uma facilidade de uso por uma interface gráfica, podemos salvar as requisições, agrupá-las, criar parâmetros, etc…

Neste treinamento utilizaremos esta ferramenta para os testes manuais.

O que é Postman

Há diversas ferramentas via interface gráfica para executar testes manuais para uma API REST. Uma das mais utilizadas no mercado é o Postman.
Com ele, além de ter uma facilidade de uso por uma interface gráfica, podemos salvar as requisições, agrupá-las, criar parâmetros, etc…

O Postman é uma ferramenta para realizar requisições HTTP. Comumente ele é utilizado para efetuar requisições em um endpoint de API REST. Estas requisições são compostas por um método HTTP e uma URL.
Com ele podemos efetuar estas requisições através de uma interface gráfica ao invés de usar ferramentas de linha de comando, de forma intuitiva e de rápido aprendizado.

Composição da interface gráfica

Tela inicial do Postman

1 - Método HTTP

Aqui você seleciona o método HTTP, onde os mais comuns são GET, POST, PUT e DELETE.

2 - URL

Aqui você insere a URL completa (URI e contexto) para a chamada da API.

3 - Parâmetros, Headers e Body

Você pode, quando necessário:

  • Inserir parâmetros na requisição (aba Params)
  • Inserir headers específicos (aba Headers)
  • Inserir dados na requisição (aba Body)

4 - ResponseBody

Uma vez enviado a requisição clicando no botão Send ao lado da URL, um retorno é gerado. Ele é chamado de Response Body e é composto pelos dados da body e status code.

5 - Histórico

Todas os envios de requisições ficam listados como histórico na aba History.

Efetuando uma requisição tipo GET

A requisição GET sempre será composta por:

  • Método HTTP GET
  • URL
  • Resposta
    * Body
    * Status

A Body, em quase 100% dos casos, retornará recursos (dados) que chamamos de Response Body.
Sempre há uma diferenciação simples e prática nas requisições GET:

  • Quando não há parâmetros no path serão retornados todos os dados do recurso
  • Quando há parâmetro somente é retornado o recurso cujo parâmetro esteja presente (Ex: ID, CPF, etc..)

Exemplo de GET no retorno de recursos

Para visualizar os dados retornados pelo uso do método GET efetuamos os seguintes passos:

  1. Preenchemos o método HTTP como GET
  2. Inserimos a URL completa da API para este método HTTP e enviamos a requisição
  3. Visualizamos o retorno (Response Body)
  4. Visualizamos o Status
Exemplo utilizando GET para retornar todos os recursos existentes

Note que o Response Body inicia com colchetes [ ]. Isso significa que temos como retorno um array de objetos (neste caso uma lista de simulações), onde cada objeto (simulação) está composto por chaves { } e separados por vírgula.

Outro ponto importante que você sempre deve verificar é se o Status corresponde ao status contido na documentação da API.

Exemplo de GET com path

Um GET com path geralmente possui um identificador para trazer um único recurso. A documentação mostra qual deve ser o identificador. No caso do exemplo abaixo o identificador é o CPF. O retorno da requisição, quando existir um recurso com o CPF informado, retornará somente aquele recurso.

Para visualizar o dado retornado pelo uso do método GET com path efetuamos os seguintes passos:

  1. Preenchemos o método HTTP como GET
  2. Inserimos a URL completa da API para este método HTTP com o identificador e enviamos a requisição
  3. Visualizamos o retorno (Repsonse Body)
  4. Visualizamos o Status
Exemplo utilizando GET pata retornar um recurso através de um parâmetro de path

Note que o Response Body inicia com chaves { }, onde é um objeto (simulação). Ele retorna o recurso para o identificador (cpf), se existente.

Efetuando uma requisição tipo POST

A requisição POST sempre será composta por:

  • Método HTTP POST
  • URL
  • Dados (Body)
  • Resposta
    • Body
    • Status

Esta requisição cria um novo recurso, por isso a obrigatoriedade de enviar os dados (Body) na requisição.

Exemplo de POST na criação de recursos

Para criar um novo recurso usando o método POST efetuamos os seguintes passos:

  1. Preenchemos o método HTTP como POST
  2. Inserimos a URL completa da API para este método HTTP
  3. Na aba Body clicamos no item raw
  4. Selecionamos o tipo de conteúdo (Content-Type) como JSON (application/json)
  5. Inserimos os dados da Body de acordo com a documentação da API e enviamos a requisição
  6. Visualizamos o retorno (Response Body)
  7. Visualizamos o Status
Exemplo utilizando POST para criar um recurso

Efetuando uma requisição tipo PUT

A requisição PUT sempre será composta por:

  • Método HTTP PUT
  • URL com identificador
  • Dados (Body)
  • Resposta
    • Body
    • Status

Esta requisição atualiza um recurso existente, por isso da obrigatoriedade de enviar os dados (Body) e um identificador na requisição.

Exemplo de PUT na alteração de recursos

Para alterar um recurso usando o método PUT efetuamos os seguintes passos:

  1. Preenchemos o método HTTP como PUT
  2. Inserimos a URL completa da API para este método HTTP com o identificador
  3. Na aba Body clicamos no item raw
  4. Selecionamos o tipo de conteúdo (Content-Type) como JSON (application/json)
  5. Inserimos os dados da Body de acordo com a documentação da API e dos dados que queremos alterar e enviamos a requisição
  6. Visualizamos o retorno (Response Body)
  7. Visualizamos o Status
Exemplo utilizando PUT paara alterar um recurso

Efetuando uma requisição tipo DELETE

A requisição DELETE sempre será composta por:

  • Método HTTP DELETE
  • URL com identificador
  • Resposta
    • Body vazia
    • Status

Esta requisição remove um recurso existente, por isso da obrigatoriedade de inserir um identificador na requisição.

Exemplo de DELETE na remoção de recursos

Para remover um recurso usando o método DELETE efetuamos os seguintes passos:

  1. Preenchemos o método HTTP como DELETE
  2. Inserimos a URL completa da API para este método HTTP com o identificador e enviamos a requisição
  3. Visualizamos o retorno (Response Body) vazio
  4. Visualizamos o Status
Exemplo utilizando DELETE para remover um recurso

Exercícios adicionais

Por favor, consulte o capítulo Exercícios adicionais para exercitar outros fluxos de exceção. Você pode fazer isso antes de avançar para o próximo capítulo ou no momento que você quiser.

Testes Automatizados para uma API REST

Com a adoção de microserviços como arquitetura para distribuir aplicações de maneira escalável, separada por responsabilidade e de maneira a entregar somente o que se propõe a aplicação de testes para esta nova camada/arquitetura se faz necessário.

Uma arquitetura baseada em microserviços é disponibilizada através de serviços (APIs) em um back-end. Estes serviços são consumidos por diferentes front-ends (web, mobile, desktop…), existindo o que chamamos de API gateway para transportar somente os dados necessários para o front-end.

É comum que os desenvolvedores criem testes unitários e de integração para suas APIs. Também é comum QAs criarem testes no front-end web ou mobile. Mas em um projeto de microserviços precisamos também testar as chamadas (requisições) e suas respostas, o que chamamos na figura abaixo de testes de api de consumo. Se não é feita a correta validação nesta camada problemas no front-end podem aparecer.

Diferente camadas de uma aplicação

Pipeline para Automação de Testes de API

Pipeline é a aplicação do processo de automação nas etapas do desenvolvimento das aplicações. Os exemplos mais clássicos são as pipelines de Continuous Integration (CI) e Continuous Delivery (CD). De modo geral é automatizar desde a compilação, execução de testes, empacotamento da aplicação e, em alguns casos, a entrega. Em um processo simples de desenvolvimento fim-a-fim teríamos a pipeline abaixo:

compilação (build) -> testes unitários -> testes funcionais -> geração da aplicação para entrega

Pipeline de automação de teste

O desenvolvimento de scripts de teste é um desenvolvimento de software. A diferença está no foco: nós estamos validando se a aplicação foi construída de acordos com os requisitos ao invés de construir a aplicação de acordo com os requisitos.

Quando criamos uma pipeline de automação de teste aplicamos os tipos de teste, que irão variar de acordo com o foco que você deseja.

Quando falamos sobre testes de API, recomendamos a seguinte pipeline:

Pipeline de Automação de uma API

Como o pipeline é uma sequência, nós recomendamos a ordem acima porque:

  • primeiro queremos saber se todos os serviços estão disponíveis (health check)
  • depois queremos saber se todos os serviços possuem os contratos esperados (contrato)
  • em seguida queremos saber se as jornadas dos usuários / cenários de uso não apresentam problemas (aceitação)
  • no fim os testes funcionais para garantir os principais caminhos e os fluxos de excessão

O que é REST-Assured

Rest-Assured é uma biblioteca Java, disponibilizada através de um DSL, para testar APIs RESTful.

Com ele podemos manipular todas as informações necessárias para criar requisições e validá-las (teste), como:

  • manipular headers e cookies
  • criar requisições através dos principais métodos HTTP
  • enviar parâmetros para requisições
  • obter o retorno das requisições
  • validar todo o tipo de dados de retorno das requisições

Você pode acessar a página principal do Rest-Assured em: http://rest-assured.io

Dependências necessárias

Para que possamos automatizar uma API REST é necessário inserir três bibliotecas nas dependências no projeto:

  • biblioteca do RestAssured
  • biblioteca do JSON Schema Validation
  • biblioteca de um framework de teste unitário (JUnit 5)

Para criar os scripts de teste automatizado utilizaremos o JUnit 5 como framework de testes unitários e suporte à criação de testes e Maven como framework de build e gerenciamento de dependências.

Você pode ou utilizar o Projeto de Teste para o desenvolvimento dos exercícios ou criar o seu próprio projeto.

Utilizando o Projeto de Teste

Eu recomendo a você fazer o clone ou download do projeto no GitHub, assim você já terá todas as dependências necessárias.

Para efetuar o clone do projeto execute o seguinte comando no seu Terminal / Prompt de Comando:

git clone https://github.com/eliasnogueira/projeto-teste-api-livro-v1.git

Exemplo git clone

Se você deseja efetuar o download do projeto:

  1. Acesse https://github.com/eliasnogueira/projeto-teste-api-livro-v1
  2. Clique no botão Code
  3. Clique no item Download zip
Download do projeto

Importe o projeto utilizando a sua IDE favorita.

Estrutura da classe de teste

Sempre que criarmos um script de teste para uma API REST com o RestAssured devemos seguir uma ordem de implementação:

  • import para uso das classes necessárias
  • criação da pré-condição do teste
  • criação do teste

Abaixo temos um exemplo de uma classe inicial para os scripts de teste automatizado para uma API REST.

 1 // imports
 2 import org.junit.jupiter.api.BeforeAll;
 3 import org.junit.jupiter.api.Test;
 4 	
 5 import static io.restassured.RestAssured.*;
 6 import static org.hamcrest.CoreMatchers.*;
 7 	
 8 class MeuTeste {
 9 	
10     // pre-condicao
11     @BeforeAll
12     static void preCondicao() {
13         baseURI = "";
14         basePath = "";
15         port = 8089;
16     }
17 	
18     // teste
19     @Test
20     void exemploDeTeste() {
21     }
22 	
23 }

Imports

No comentário //imports, além dos imports para o teste e pré-condição (os dois primeiros) temos dois imports muito importantes. Eles são responsáveis por usos específicos de funcionalidades. Entenda-os através dos comentários no final de cada linha do código abaixo:

1 import static io.restassured.RestAssured.*;
2 import static org.hamcrest.CoreMatchers.*;

Pré-Condição

Sempre que executarmos um script de teste para uma API REST o endpoint pode ser diferente, ou a porta, ou mesmo a URL inicial da aplicação. Para isso criamos um método de pré-condição no teste. O uso do @BeforeAll faz com que essa pré-condição seja executada uma vez para a classe (script de teste).

Dentro da pré-condição devemos informar ao Rest-Assured a URL completa para efetuar uma requisição. Utilizaremos os seguintes atributos:

  • baseURI: URL inicial sem a aplicação (contexto)
  • basePath: nome do componente (contexto) da aplicação
  • port: porta onde a aplicação está sendo executada

No exemplo abaixo os atributos baseURI e basePath estão vazios. Eles serão explicados no próximo tópico.

1 // pre-condicao
2 @BeforeAll
3 static void preCondicao() {
4    baseURI = "";
5    basePath = "";
6    port = 8089;
7 }

Teste

O método de teste que irá executar as ações necessárias na API, a fim de validá-la.

1 // teste
2 @Test
3 void exemploDeTeste() {
4 }

Sobre a abordagem de criação de teste adotada neste livro

No geral podemos utilizar duas abordagens: uma classe de teste por teste ou diversos testes em uma classe de teste.

A abordagem uma classe de teste por teste tem o intuito de criar uma classe e apenas um método de teste dentro desta classe. Cada novo teste deve ser uma nova classe.

1 class UmaClasseDeTestePorTeste {
2 	
3     // você terá apenas um único teste na classe
4     @Test
5     void unicoTesteNaClasse() {
6     }
7 	
8 }

Benefícios

  • Maior controle sobre os diferentes testes criados

Desvantagens

  • Maior manutenção
  • Maior gestão de pre e pós condições
  • Maior quantidade de classes de teste

A abordagem diversos testes em uma classe de teste tem o intuito de criar uma classe de teste e ter diversos métodos de teste dentro dela.

 1 class DiversosTestesEmUmaClasse {
 2 	
 3     // você pode ter
 4     @Test
 5     void verificaSubmissaoComSucesso() {
 6     }
 7     
 8     // quantos testes
 9     @Test
10     void verificaUmaRegraDeNegocio() {
11     }
12     
13     // forem necessários
14     @Test
15     void verificaUmCenarioNegativo() {
16     }
17 	
18 }

Benefícios

  • Manutenção centralizada
  • Menor gestão de pré e pós condições
  • Co-relação entre testes com o mesmo propósito
  • Centralização de testes para uma mesma funcionalidade

Desvantagens

  • Maior gestão na criação de suites de teste

Testes Funcionais para uma API REST com RestAssured

O Rest-Assured trabalha com uma estrutura de métodos chamada de DSL - Domain Specific Language que visa facilitar a criação de testes usando métodos que expressam o que queremos fazer no script de teste.

Quando utilizamos uma DSL podemos encadear a utilização de métodos sempre inserindo um ponto . seguido pelos próximos métodos que desejarmos utilizar ao invés de inserir cada método em uma nova linha. Esta abordagem usa o design pattern Fluent Builder para facilizar a utilização de vários métodos

A estrutura básica dos comandos do Rest-Assured se dá por:

Método Descrição
given() Pré-condições de uma requisição para a API. Não é obrigatório
when() Ações que a API deve executar. Leva obrigatoriamente o método HTTP
then() Resultados esperados da requisição. Inserimos as validações dos resultados obtidos os extraimos o resultado para uso

Comandos básicos

Given

Sempre utilizado através do método RestAssured.given() ou given() quando possuimos o import estático import static io.restassured.RestAssured.*;

É a pré-condição da requisição. Existem alguns tipos de pré-condições que podem ser necessárias em uma requisição, como:

  • autenticação
  • parâmetros
  • cookies
  • headers
  • configurações do recurso
  • content-type
  • body

When

Sempre utilizado através do método RestAssured.when() ou when() quando possuimos o import estático import static io.restassured.RestAssured.*;

Ele é obrigatório porque, praticamente, inicia a requisição. Devemos informar qual o verbo HTTP utilizaremos para efetuar a requisição. Existem diversos métodos HTTP, porém no geral utilizaremos:

Metodo HTTP Exemplo
GET when().get()
POST when().post()
PUT when().put()
DELETE when().delete()
Parâmetros nos verbos HTTP

Normalmente passamos o nome do recurso como parâmetro para o método HTTP. Internamente o Rest-Assured usará a URL completa para efetuar a requisição baseada em: http:// + baseURI + port + basePath + recurso. Se desejarmos fazer uma chama GET teremos:

Atributo Valor
chamada GET http://localhost:8089/api/v1/simulacoes
baseURI http://localhost
port 8089
basePath /api/v1
recurso /simulacoes

Como nós definiremos os valores dos atributos para baseURI, port e basePath informaremos apenas o recurso no uso do método HTTP. O Rest-Assured se encarregará de fazer a composição da URL para enviar a requisição.

1 when().
2    get("/simulacoes")

Then

Sempre utilizado através do método then() após utilizarmos qualquer método HTTP utilizado no método when().

É associado aos resultados esperados de uma requisição ou extração de dados. Nenhum dos métodos de validação é obrigatório, mas o uso de alguns são altamente recomendados.

São métodos disponíveis para o then:

Método Descrição
statusCode valida o status code do retorno
body valida dos dados de retorno da body
contentType valida o content-type de retorno
header valida o header retornado
cookie valida alguma informação em cookies

Exemplo completo de script

Apenas para você ter uma base, este seria um script completo para uma requisição GET.

 1 import org.junit.jupiter.api.BeforeAll;
 2 import org.junit.jupiter.api.Test;
 3 
 4 import static io.restassured.RestAssured.*;
 5 import static org.hamcrest.CoreMatchers.*;
 6 
 7 class ExemploGetTest {
 8 
 9     @BeforeAll
10     static void preCondicao() {
11         baseURI = "http://localhost";
12         basePath = "/api/v1";
13         port = 8089;
14     }
15 
16     @Test
17     void exemploDeTeste() {
18         when().
19             get("/simulacoes").
20         then().
21             statusCode(200).
22             body("cpf", equalTo("66414919004"));
23     }
24 }

Efetuando uma requisição GET sem parâmetros

A primeira coisa que temos que analisar, não somente em uma requisição do tipo GET, mas em todos os tipos (métodos HTTP) é se teremos alguma pré-condição. Se não houver uma pré-condição o início do script sempre usará função when().

Para efetuar uma requisição GET utilize o método get(). Não esqueça que devemos sempre passar por parâmetro o recurso da requisição.

No Exemplo completo do script podemos ver uma requisição GET para o recurso simulacoes na linha 19.

Como validar os resultados esperados

Um script de teste automatizado só pode ser chamado assim se este conter formas de validar os resultados esperados de sua execução. Para que possamos validar os resultados esperados será necessário usar o método then() logo após a chamada do método HTTP.

1 when().
2     get("/simulacoes").
3 then().
4     // validações dos resultados

É uma boa prática validar, pelo menos, dois itens do retorno da requisição:

  • status code: código de status retornado pela requisição
  • body: dados retornados pela requisição

Um exemplo de código com validações seria:

1 when().
2     get("/simulacoes").
3 then().
4     statusCode(200).
5     body(algumaCoisaAqui);

No statusCode inserimos como parâmetro o código esperado pelo retorno do resultado.

No body inserimos a validação, que será explicada abaixo.

Body como unico objeto

O método body refere-se aos dados de retorno da requisição, que geralmente está no formato JSON. Digamos que tenhamos o seguinte retorno da body de uma requisição do tipo GET:

1 {
2     "nome": "João",
3     "idade": 27 
4 }

O método body do Rest-Assured espera receber dois parâmetros:

  • atributo da body
  • tipo de validação

O atributo da body é o atributo que é apresentado no retorno de dados da body e deve estar sempre entre aspas duplas " ". No exemplo que temos seria o nome ou idade.

O tipo de validação é a forma que o valor retornado será validado. Para que o script possa fazer essa validação utilizamos métodos do Hamcrest chamados de matchers.

Na grande maioria dos casos utilizamos ou o matcher is() ou o matcher equalTo(). Semanticamente os dois fazem a mesma coisa: validam se um resultado é ou é igual a. Para ambos precisamos inserir como parâmetro o valor esperado.

O script abaixo mostra um exemplo de validação de dados.

1 @Test
2 public void exemploValidacaoDados() {
3     when().
4         get("/simulacoes").
5     then().
6         statusCode(200).
7         body("nome", is("João")).
8         body("idade", is(27));
9 }

Note que, para cada atributo, teremos uma linha com o método body.

Body como um array de objetos

Teremos o caso de um body como um array de objetos quando:

  • a body inicia e termina com colchetes
  • a body possui objetos separados por vírgula (ou não no caso de apenas um registro)

O exemplo abaixo apresenta um retorno de body como um array contendo dois objetos:

 1 [
 2     {
 3         "id": 11,
 4         "cpf": "66414919004",
 5         "email": "fulano@gmail.com",
 6         "valor": 11000,
 7         "parcelas": 3,
 8         "seguro": true
 9     },
10     {
11         "id": 13,
12         "cpf": "12345678910",
13         "email": "deltrano@email.com",
14         "valor": 40000,
15         "parcelas": 4,
16         "seguro": true
17     }
18 ]

Quando este tipo de situação ocorrer o primeiro parâmetro da body, que é o atributo da body, deve conter o índice do array + valor do atributo no seguinte formato:

[indice].atributo

O exemplo abaixo demonstra como inserir o índice para validar os atributos. No lado esquerdo há dois objetos como retorno de um array. Como qualquer contagem de um array começa em zero (0), a primeira posição do array é zero. Note que, no lado direito, há as validações para cada um dos atributos utilizando o índice zero do array [0]. Para a segunda posição o índice do array é um (1), logo no segundo conjunto de validações é utilizado o índizce um do array [1].

Explicação sobre validação de dados em um array

Observações

Observação 1

Para o item 4.1 a chamada do get deve ver: when().get("/<RECURSO>"), sendo o <RECURSO> ubstituito pelo recurso descrito neste passo (simulacoes).

Observação 2

Para as validações da body lembre-se que o primeiro parâmetro é o atributo. Porém devemos iniciar com o índice do array, pois o retorno de dados é um array. Exemplo:

1  body("[0].cpf", equalTo("66414919004")

Observação 3

Para a validação do atributo valor, adicione um “efe” (f) ao final do número. Devemos inseri-lo para informar ao script que o tipo de dado é um número de ponto flutuante (float). Se você não inserir esta letra a validação vai falhar, mesmo o resultado da falha apresentando valores idênticos para resultado esperado e resultado obtido.

Resultados Esperados

  • Script executado com sucesso (sem nenhum erro de validação ou qualquer outro)


// imports
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.*;

class MeuTeste {

// pre-condicao
@BeforeAll
static void preCondicao() {
baseURI = “”;
basePath = “”;
port = 8089;
}
// teste
@Test
void exemploDeTeste() {
}

}

Efetuando uma requisição GET com parâmetros de path

Exercício: criar uma requisição GET com parâmetros de path

Efetuando uma requisição GET com parâmetros de query

Exercício: criar uma requisição GET com parâmetros de query

Criação de um BaseTest

Exercício: criar um BaseTest

Efetuando uma requisição POST

Exercício: criar uma requisição POST

Criação de um Builder

Exercício: criar uma requisição POST usando o builder para gerar os dados

Efetuando uma requisição PUT

Exercício: criar uma requisição PUT

Exercício: criar uma requisição PUT recuperando dados

Efetuando uma requisição DELETE

Exercício: criar uma requisição DELETE

Criando um DELETE sem dependência de dados

Exercício: criar uma requisição DELETE sem dependência de dados

Testes de Health Check para uma API REST

Exercício: criar um teste de Health Check

Testes de Contrato para uma API REST

Gerando um JSON Schema

Como aplicar um teste de contrato

Diferentes versões de API - parte 1

Diferentes versões de API - parte 2

Exercício: criar um teste de contrato