Sumário
- Prefácio
- O básico
- Explicações gerais
- Documentação de uma API REST
- Testes Manuais para uma API REST
-
Testes Automatizados para uma API REST
- Pipeline para Automação de Testes de API
- O que é REST-Assured
-
Testes Funcionais para uma API REST com RestAssured
- Comandos básicos
- Efetuando uma requisição GET sem parâmetros
- Como validar os resultados esperados
- Efetuando uma requisição GET com parâmetros de path
- Efetuando uma requisição GET com parâmetros de query
- Criação de um BaseTest
- Efetuando uma requisição POST
- Criação de um Builder
- Efetuando uma requisição PUT
- Efetuando uma requisição DELETE
- Criando um DELETE sem dependência de dados
- Testes de Health Check para uma API REST
- Testes de Contrato para uma API REST
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:
- Java JDK maior ou igual a versão 8
- Git
-
Maven
* Configure o Maven no PATH do seu sistema operacional no Windows ou no Mac - Browser Web moderno nas versões mais recentes
* Recomendo o Google Chrome - IDE de Desenvolvimento
* Recomendo utilizar o Intellij IDEA Community Edition - Postman
-
Lombok plugin instalado na sua IDE favorita
1. Configure o plugin
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
- Abra o Prompt de Comando (Windows) ou o Terminal (Mac ou Linux)
- Navegue até um diretório de sua escolha
- Execute o seguinte comando
git clone https://github.com/eliasnogueira/credito-api.git
- Navegue, via Prompt de Comando ou Terminal, até o diretório do projeto
cd credito-api
- Execute o comando
mvn compile
e aguarde até o término do build
Projeto de Testes
- Abra o Prompt de Comando (Windows) ou o Terminal (Mac ou Linux)
- Navegue até um diretório de sua escolha
- Execute o seguinte comando
git clone https://github.com/eliasnogueira/projeto-teste-api-livro-v1.git
- Navegue, via Prompt de Comando ou Terminal, até o diretório do projeto
cd projeto-teste-api-livro-v1
- Execute o comando
mvn compile
e aguarde até o término do build - Abra a sua IDE de Desenvolvimento
- Nela, selecione a opção de importação de projetos para o tipo Maven apontando para a pasta projeto-teste-api-livro-v1
- 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 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 sucesso
Uma vez finalizada a simulação o usuário visualiza uma tela 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.
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
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
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:
- Método HTTP
- URL da requisição
- Descrição da requisição
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:
- Parâmetros
- Respostas
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:
- nome do atributo
- tipo de dados + tipo do parâmetro
- obrigatoriedade
- descrição
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:
- O Code (status code) como 200 para uma pessoa que possui restrição
- O exemplo de retorno (Response Body) como
* Logo, quando efetuarmos uma requisição para um CPF que possui uma restrição, receberemos estas duas informações.
- O Code como 404 para uma pessoa que não possui restrição e sem retorno (sem Response Body)
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:
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.
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:
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.
POST /api/v1/simulacoes
Esta requisição cria um recurso (neste caso uma simulação).
Note o seguinte na sessão Parameters:
- O parâmetro esperado é uma simulacao e o tipo é um body.
- Quando isso acontecer você deve enviar os mesmos atributos contidos no Exemple Value
- 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
Note também que há diferentes retornos na seção Responses.
Quando a simulação for criada com sucesso notamos que:
- O status code retornado deve ser 201
- Não há respose body, logo não precisamos validá-lo
- É 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
.
Existem outros dois retornos na seção Responses.
- Recebemos o status code 409 quando o CPF do objeto que estamos enviando já existe
- Recebemos o status code 422 quando existe a falta de alguma informação ou alguma regra de negócio é violada
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:
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.
- Devemos informar o
cpf
como chave de pesquisa como um path parameter - Devemos informar o objeto
simulacao
- Este objeto pode ser os atributos descritos no Example Value
- O tipo de conteúdo deve ser application/json
A documentação do PUT
apresenta três possíveis retornos:
- Quando a alteração for executada com sucesso o status code será 200
- O response body da simulação alterada com sucesso traz todos os atributos da simulação mesmo se não tiverem sido alterados
- Quando a alteração para a simulação com CPF desejado não existir, o status code será 404
- Se enviarmos uma simulação com um CPF alterado, e este já existir, o retorno será o status code 409
DELETE /api/v1/simulacoes/{cpf}
Esta requisição remove uma simulação existente. A chave de pesquisa da simulação existente é o CPF.
- Para remover a simulação devemos informar o
cpf
como parâmetro no path (path parameter)
O DELETE
tem dois possíveis retornos:
- O status code 204 quando a simulação foi removida com sucesso
- O status code 404 quando uma simulação com o CPF informado não foi encontrado
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
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:
- Preenchemos o método HTTP como
GET
- Inserimos a URL completa da API para este método HTTP e enviamos a requisição
- Visualizamos o retorno (Response Body)
- Visualizamos o Status
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:
- Preenchemos o método HTTP como
GET
- Inserimos a URL completa da API para este método HTTP com o identificador e enviamos a requisição
- Visualizamos o retorno (Repsonse Body)
- Visualizamos o Status
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:
- Preenchemos o método HTTP como
POST
- Inserimos a URL completa da API para este método HTTP
- Na aba Body clicamos no item raw
- Selecionamos o tipo de conteúdo (Content-Type) como JSON (application/json)
- Inserimos os dados da Body de acordo com a documentação da API e enviamos a requisição
- Visualizamos o retorno (Response Body)
- Visualizamos o Status
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:
- Preenchemos o método HTTP como
PUT
- Inserimos a URL completa da API para este método HTTP com o identificador
- Na aba Body clicamos no item raw
- Selecionamos o tipo de conteúdo (Content-Type) como JSON (application/json)
- Inserimos os dados da Body de acordo com a documentação da API e dos dados que queremos alterar e enviamos a requisição
- Visualizamos o retorno (Response Body)
- Visualizamos o Status
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:
- Preenchemos o método HTTP como
DELETE
- Inserimos a URL completa da API para este método HTTP com o identificador e enviamos a requisição
- Visualizamos o retorno (Response Body) vazio
- Visualizamos o Status
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.
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:
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
Se você deseja efetuar o download do projeto:
- Acesse https://github.com/eliasnogueira/projeto-teste-api-livro-v1
- Clique no botão Code
- Clique no item Download zip
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.
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:
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.
Teste
O método de teste que irá executar as ações necessárias na API, a fim de validá-la.
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.
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.
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.
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.
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.
É 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:
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:
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.
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:
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]
.
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: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() {
}
}