Em um artigo anterior, compartilhei minha experiência de automação no Robot Framework. Agora falaremos sobre uma abordagem ligeiramente diferente para testar a API para um projeto no Kotlin.
Aproveitando a liberdade de escolher a pilha de tecnologia e confiando no desejo de tentar algo novo "em batalha", virei-me para Descanse em paz. Não sem dificuldades, meus colegas e eu lançamos testes e, como resultado do desenvolvimento da abordagem, a registramos na lista de tarefas principais para esse tipo de tarefa.
(imagem usada como paródia)Tudo começou com o fato de uma solicitação ter sido recebida para automatizar o teste da API em um dos novos projetos no
segmento de Ad-tech . Fizemos o Campaign Manager, DMP e várias integrações com sistemas de terceiros. E os testadores tiveram uma tarefa extremamente simples: automatizar os testes de fumaça para maior integração ao IC e monitoramento constante do estado dessa API, uma vez que sistemas de terceiros estão ligados a ela e a operação correta é fundamental. A verificação da lógica de negócios nesse caso não foi necessária, pois o serviço testado é essencialmente uma interface proxy.
Por que ter certeza?
Do ponto de vista da automação de teste, conseguimos trabalhar em uma variedade de áreas - interface do usuário, web, móvel, desktop, back-end, API REST, SOAP.
O projeto anterior nos deu muita experiência no Robot Framework, por isso seria mais lógico usá-lo. A maioria da equipe de teste está familiarizada com ele e, se for necessária uma substituição urgente, isso será feito de forma rápida e praticamente indolor. Mas uma decisão diferente foi tomada.
Primeiro, o projeto em si foi escrito em Kotlin e construído usando Gradle. Ao mesmo tempo, na fase de design, os autotestes foram decididos para não alocar um projeto separado. Conforme observado nos comentários do artigo anterior, juntar Java (Kotlin) e o Robot Framework é uma grande dor, portanto, não havia sentido em se referir à RF. Além disso, pensando em trabalhar com um robô, eu estava interessado em tentar uma abordagem diferente. Além disso, nesse projeto, pudemos escolher independentemente uma pilha de tecnologia - a empresa não estabeleceu suas próprias condições.
Em busca de idéias, procurei nossa equipe de desenvolvimento, assim como colegas de teste, e o CTO
me aconselhou a olhar para o Rest-Assured (
rest-assured.io ). Depois de ler a documentação e os testes de amostra em fontes abertas, a abordagem proposta pela Rest-Assured pareceu muito atraente para mim. Significa receber uma resposta do back-end na forma de JSON, que desserializamos para bons objetos Java antigos.
Além disso, com essa estrutura, você pode trabalhar como com um objeto normal. Como resultado, temos uma abordagem orientada a objeto completamente normal para validar a correspondência da resposta da API à estrutura de objeto descrita. Como bônus, obtemos um teste rápido à prova de falhas da estrutura de resposta - se o objeto recebido desserializando a resposta da API diferir no número de campos ou em seus nomes, os testes serão eliminados antes mesmo de verificar os dados com um erro de desserialização. Portanto, entenderemos se precisamos reparar o back-end ou atualizar os testes de acordo com os novos requisitos da API. No nosso caso, isso foi importante porque, como mencionei acima, muitos subsistemas de terceiros estão vinculados à API.
Para maior precisão, observo que aproximadamente o mesmo teste à prova de falhas pode ser obtido na RF, mas de uma maneira ligeiramente diferente. No entanto, a história hoje não é sobre isso. Pessoalmente, era mais conveniente e compreensível para mim entrar do lado da Rest-Assured com uma entidade que possui determinados campos e métodos.
Teste de penas
Antes de começar a usar a pilha em um projeto real, decidi experimentá-lo em um pequeno serviço CRUD. Para não praticar imediatamente seus próprios erros, ele decidiu procurar as melhores práticas nessa pilha e encontrou rapidamente
um artigo de Philip Hauer , onde refletiu sua experiência em automação na Rest-Assured. Depois de estudar o artigo, não foi difícil escrever uma versão funcional dos testes do meu serviço. Acabaram simples, fáceis de ler e entender.
Para a batalha!
O projeto começou e, quando as primeiras estruturas de resposta foram descritas, a preparação da documentação do teste e a redação dos autotestes começaram. Para entender o panorama geral, darei uma pilha completa de autotestes:
- Java
- JUnit4 - execução e parametrização de scripts de teste (anotação padrão @Parametrize),
- Garantido - criando e executando consultas,
- AssertJ - validação dos valores recebidos,
- Allure - criação de relatórios,
- Gradle - montagem
- Jackson - desserialização.
Quando os primeiros testes foram escritos, o problema da parametrização adequada tornou-se aparente. Transmitir valores simples de dados e o resultado esperado parecia extremamente ineficiente e feio. Eu não tinha tempo para resolver esse problema antes das férias, mas meus colegas, que estavam envolvidos durante a minha ausência, resolveram o problema. Para uma parametrização bonita e fácil de ler, eles decidiram criar uma classe base com as funções de adicionar, receber e excluir parâmetros de objeto, dos quais todas as classes cujos objetos foram usados para chamar os métodos API correspondentes foram herdadas.

Isso permitiu literalmente em tempo real criar os dados necessários e transmiti-los nos parâmetros de teste.

O Rest-Assured permite desserializar a resposta para a classe necessária. A resposta resultante é decomposta em uma estrutura conhecida anteriormente usando Jackson. As classes para desserialização parecem tão simples quanto possível - todos os campos esperados com seus tipos são descritos.

Além disso, o trabalho simples com objetos e a afirmação de campos específicos e seus valores continuam.
A coisa mais difícil e não óbvia que encontrei nessa abordagem é a incapacidade de passar nulo para Rest-Assured como um dos parâmetros (o resultado é uma queda no NullPointerException). Aparentemente, esse é um problema conhecido, mas o desenvolvedor não corrigirá a situação, recomendando enviar o campo vazio ou não enviá-lo. Já enfrentamos esse problema no estágio em que a base do projeto estava pronta e restava apenas adicionar classes de teste. Portanto, apenas corrigimos levemente nosso código.
Em geral, gostei da abordagem. É curioso que, após algum tempo, tenha começado a ser aplicado no projeto de outro cliente (embora não do nosso suprimento). E colocamos a pilha montada para APIs de teste de automação (JUnit + AssertJ + Rest-Assured) na categoria das principais para projetos Java / Kotlin. No Python, puxá-lo será contra-intuitivo, pois é melhor que as competências de desenvolvimento e teste se sobreponham.
Também é importante notar que uma das vantagens desta solução é sua escalabilidade e adaptabilidade a lógica complexa. Isso significa que é mais adequado para projetos sérios nos quais realmente faz sentido descrever objetos grandes, código limpo e arquitetura de blocos, quando existem partes separadas responsáveis pela preparação, transmissão e recebimento de dados de forma legível, bem como verificação subseqüente de dados para conformidade. algumas condições. Em pequenos projetos, tudo isso não faz sentido.
Sumário
Em cerca de um mês, conseguimos automatizar cerca de 250 testes e fornecer o nível de cobertura necessário. Obviamente, após o término do processo de automação, foi realizada uma apresentação interna para todos, para que os funcionários tivessem uma idéia do trabalho realizado e pudessem aprender com a experiência adquirida nesse projeto.
Autor do artigo: Dmitry Masters
PS Publicamos nossos artigos em vários sites do Runet. Assine nossas páginas no
canal VK ,
FB ou
Telegram para descobrir todas as nossas publicações e outras notícias do Maxilect.