!!!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.
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 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.
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.
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:
Método HTTP
URL da requisição
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:
Parâmetros
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:
nome do atributo
tipo de dados + tipo do parâmetro
obrigatoriedade
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:
O Code (status code) como 200 para uma pessoa que possui restrição
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.
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:
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
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:
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.
Retorno de sucesso
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
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.
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
Parâmetros necessário para o PUT
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
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.
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:
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
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:
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:
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
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:
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
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:
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
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:
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
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:
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
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
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:
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:
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.
O método de teste que irá executar as ações necessárias na API, a fim de validá-la.
1 // teste2 @Test3 voidexemploDeTeste(){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 classUmaClasseDeTestePorTeste{2 3 // você terá apenas um único teste na classe4 @Test5 voidunicoTesteNaClasse(){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.
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.
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
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":274 }
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.
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)