Automação de testes de ponta a ponta de um sistema de informação integrado. Parte 2. Técnico

Com este artigo, continuamos uma série de publicações sobre como automatizamos em um dos principais projetos da LANIT o processo automático de teste manual (doravante denominado autoteste) de um grande sistema de informação (doravante referido como Sistemas) e o que veio dele.

A segunda parte da publicação é focada principalmente nos líderes dos grupos de automação de testes de interface do usuário de ponta a ponta e na automação de teste líder. Aqui eles encontrarão receitas específicas para a organização arquitetônica de código e implantação, que suporta o desenvolvimento paralelo em massa de grandes grupos de testes em face da constante variabilidade das especificações de teste. Esta parte contém a lista completa de funções necessárias para testes de interface do usuário com alguns detalhes de implementação, bem como uma lista de surpresas que você pode encontrar.

Aqui você encontrará a Parte 1. (Por que precisamos de automação. Organização do processo de desenvolvimento e gerenciamento. Organização de uso)

Fonte

Pilha de arquitetura e tecnologia


Estrutura geral do sistema e seu ambiente - Design de Alto Nível


As principais tecnologias e bibliotecas usadas no projeto:

  • JavaSE8 & maven & JUnit - pilha de desenvolvimento;
  • Selenium - uma biblioteca para automatizar as ações de um navegador da web;
  • Selenide - um complemento para o Selenium, que possui uma API elegante que simplifica bastante a interação com os elementos do navegador e da página;
  • Selenoid & GGR - implementação do Selenium Grid e um balanceador de carga para execução de testes em um servidor de CI + contêineres pré-configurados com navegadores;
  • Yandex Allure - para relatórios no servidor de IC.

Um diagrama geral dos componentes dos autotestes e da infraestrutura Selenoid é mostrado nos diagramas abaixo, incluindo comentários explicativos:

Estrutura de autoteste
Aplicativo para automação da regressão da interface do usuário.
Entregue no código fonte. Usa JUnit4

run.properties
Arquivo de configuração para executar os Autotestes. Define o nome condicional do suporte usado e o tipo de execução - local ou por meio de contêineres externos e outras variáveis.

Allure plugin
Um arquivo executável especial que é instalado em um servidor Bamboo.
Cria um relatório HTML de teste acessível por meio de um servidor Bamboo.

Relatório de teste
Teste o relatório HTML disponível no servidor Bamboo.
Ele é armazenado no servidor Bamboo nos resultados do plano em uma guia separada.

Bambu
Fornece o lançamento de testes de integração nos modos automático e manual.
Armazena relatórios de teste no formato Allure.

ggr-server
Balanceador de servidor de servidores Selenoid.
Fornece balanceamento de solicitações de autotestes (RemoteWebDriver) para várias instâncias de servidores selenium.

Docker
Servidor Docker para executar contêineres e navegadores do servidor Selenoid.

Selenoid-server
Servidor de Teste Remoto
Fornece o lançamento de testes em contêineres docker especializados, usando um navegador "sem cabeça".
Executa testes no modo paralelo de acordo com o número especificado de threads simultâneos.

Selenoid-ui
Interface do usuário do servidor para o servidor Selenoid.
Permite o monitoramento on-the-fly do progresso do teste através do VNC.

Selenoid-webdriver
Um contêiner especializado com um navegador sem cabeça para executar testes remotos.
Fornecido a partir do repositório Selenoid.

Gitlab
Armazena o código-fonte do aplicativo Autotest.


Esquema de trabalho


O diagrama a seguir mostra o esquema geral do serviço de Testes Automáticos no nível de Design de Alto Nível.


Instalação e Implantação


Os autotestes são baseados em quatro servidores, um dos quais é o servidor de execução e os outros três fornecem o lançamento de navegadores sem cabeça em contêineres. O poder atual dos autotestes fornece 60 fluxos simultâneos e pode ser expandido, se necessário.

O diagrama de implantação atual é mostrado no diagrama a seguir. Em geral, se você não precisar de mais de 20 navegadores simultâneos (threads de teste), é bem possível colocar tudo em um servidor 12 Kernels + 24 RAM. Começamos com essa configuração, mas à medida que os requisitos para o poder dos autotestes aumentavam, descobrimos empiricamente que a configuração mais estável e econômica é um servidor "típico" de 12 Kernel + 24 RAM.

Em nossa experiência, para testar sistemas com uma interface da Web no Angular, a configuração aceitável do contêiner com o navegador deve ser o piso do kernel + GB de memória. Uma configuração menor torna o navegador mais lento e pode até causar falhas não identificáveis.


Estrutura e organização do código


Antes de passar para a estrutura e organização do código, listarei mais uma vez as políticas e restrições que finalmente determinaram a direção do desenvolvimento da arquitetura:

  • Um grande número de scripts complexos de teste de ponta a ponta. Alta intensidade de desenvolvimento;
  • alta volatilidade dos cenários de teste (sua variabilidade);
  • alta velocidade contínua de entrega do cenário (desenvolvimento e revisão);
  • qualificações iniciais dos desenvolvedores de autotestes;
  • Alto desenvolvimento planejado de desenvolvedores.

Com base no exposto, os principais drivers de arquitetura são os seguintes:

  • garantir código altamente estruturado para facilitar a legibilidade e a gerenciabilidade;
  • separação e isolamento máximos entre implementações de aplicativos de testes específicos e classes transversais de funcionalidade comum;
  • redução máxima das dependências necessárias para facilitar a mudança;
  • Reutilização razoável de código no nível de classes transversais com possível duplicação de código possível no nível de grupos de teste independentes (subsistemas funcionais) para reduzir dependências e mesclar conflitos no nível de desenvolvimento;
  • rejeição de estruturas complexas, como spring, aspectJ para reduzir o tempo de "entrada" de desenvolvedores iniciantes no projeto.

Em geral, essa abordagem arquitetônica tornou possível implementar o desenvolvimento rápido independente sob condições de alta variabilidade de cenários, com um processo praticamente contínuo de entrega de novos testes ao produtivo. Arquitetura e agora suporta com êxito o "carregamento e refinamento", apesar do fato de o sistema já ter implementado mais de 1.500 cenários de negócios.

A estrutura geral do código e as descrições das principais soluções específicas são fornecidas abaixo.

Estrutura geral do código e padrões de desenvolvimento


A estrutura geral da organização do código é baseada em uma arquitetura "em camadas" (consulte o diagrama), que geralmente herda o padrão de página recomendado pelos desenvolvedores do Selenium. Expandimos esse padrão adicionando um nível de elementos da Web à base e destacando o nível do cenário de teste:

  • nível de classe de teste
  • nível do cenário de teste
  • nível da página da web
  • nível do elemento da web
  • estrutura de teste (mostrada escurecendo no diagrama).

Cada nível neste esquema possui um conjunto específico de responsabilidades e funcionalidade. A interseção funcional entre camadas ou dependências ilegítimas entre elas não era permitida e era o principal motivo do retorno do commit para revisão como parte de uma revisão de código antes da solicitação de mesclagem.

Além disso, uma estrutura "em camadas" foi corrigida na divisão do código em pacotes (veja o diagrama abaixo).

Esse esquema tornou possível dividir todos os subsistemas (dos quais havia significativamente mais do que no esquema) entre os desenvolvedores e, como resultado, possibilitou o desenvolvimento independente com um número praticamente desaparecendo de conflitos de mesclagem.

O diagrama abaixo mostra a estrutura geral da implementação da classe de teste e o esquema de distribuição para a implementação dos pacotes do projeto.



Nível de classe de teste


O nível da classe de teste inclui uma única classe para um teste individual específico (o teste é descrito no sistema de gerenciamento de testes como uma sequência linear de scripts de teste). A classe de teste é uma classe Junit com a anotação @ Test do método correspondente. Em geral, uma classe de teste implementa apenas um método de teste.

Cada classe de teste herda de uma classe base cuja tarefa é inicializar todos os TestRules no RuleChain, ou seja, inicializar uma infraestrutura de teste, como inicializar um driver da web, definir diretórios temporários e outros manipuladores de utilidade geral.

A classe de teste é responsável por organizar a execução do cenário de teste por meio de:

  1. inicialização de dados comerciais de teste (cena de teste),
  2. chamada seqüencial de scripts de teste individuais (ações) de acordo com o cenário necessário com a transferência de dados de teste inicializados para eles a partir da etapa 2.

Além disso, na etapa 2, deve haver uma sequência linear sem ramificações ou pontos de decisão. O modelo de implementação de classe é mostrado abaixo.

Nível do caso de teste


O nível do caso de teste é responsável pela implementação de casos de teste específicos, conforme descrito. Um cenário de teste nesse contexto é uma sequência de operações executadas por um usuário em um sistema através da interação com elementos nas páginas da web do aplicativo.

O principal objetivo da classe de caso de teste:

  • execute a sequência especificada do script de teste acessando os métodos das classes mais baixas de Page, usando

    o dados transmitidos da cena de teste,
    o dados recebidos de páginas da web (de classes de páginas);
  • Execute os testes de negócios necessários para obter sucesso no teste no contexto do modelo de negócios e da cena do teste. Em outras palavras, a classe de caso de teste NÃO verifica a marcação, disponibilidade e acessibilidade dos elementos, mas opera no modelo de negócios da cena de teste, na verdade conduzindo testes de negócios.

A classe do caso de teste é organizada como uma "interface funcional":

  • contém apenas um método público "apply" (@ Step), que:

    o fornece a implementação do script como uma chamada para uma sequência de "ações" (métodos fornecidos pelas classes Page),
    o aceita todos os objetos de negócios necessários como entrada, enquanto NÃO CRIA nenhum objeto de negócios e NÃO INTERATA diretamente com nada além de classes de página;
  • contém X métodos privados (@ Step), cada um dos quais implementa uma etapa separada específica do script de teste (conforme descrito em TMS - Test Management System);
  • Não interage (não chama métodos) com outras atividades, mesmo atividades de um subsistema semelhante;
  • não aceita entrada e não opera com dados de login. Em outras palavras, não sabe nada sobre os papéis a partir dos quais é lançado.

Esta organização de classe permite:

  • Forneça relatório de auto-documentação. Cada método corresponde a um ponto específico na especificação de teste e é anotado pela anotação allure @ Step;
  • reutilize as classes de script de teste em testes diferentes, uma vez que o script de teste 1) não cria uma cena de teste e 2) não depende do logon do usuário (a operação de logon de logon é executada no nível da classe de teste) 3), o teste não depende de outras classes scripts.

Nível da página da Web


O nível da página da Web é um padrão de página clássico para teste no Selenium. A classe de página contém definições de elementos específicos da Web e um conjunto de métodos públicos para executar determinadas ações de grupo no contexto das etapas do caso de teste.

A classe de página é diretamente responsável por inserir dados de negócios em elementos específicos da interface, trabalhando com o driver da web (iniciando o JS, por exemplo) e, consequentemente, testando formatos, verificando o layout e a estrutura da página da Web para verificações básicas como: o elemento não está encontrado / não disponível e o item não contém o valor necessário.

A classe de página também não inclui e não pode acessar outras páginas e seus métodos. Além disso, as classes de página não executam verificações de negócios, limitando-se apenas às verificações estruturais no nível dos elementos da Web no caso geral, fornecendo "até" os dados no nível "script de teste" recebidos das páginas.

Nível do elemento da Web


A camada de elementos da web inclui uma biblioteca de elementos finitos que podem estar em uma página da web. Inclui primitivos específicos e conglomerados mais complexos, consistindo em vários elementos que chamamos de widgets. Exemplos de widgets podem ser construções como “pajinators”, menus globais, vários quadros e janelas modais, elementos complexos como YandexMap ou YouTube player. Os elementos da Web organizam a composição com classes de página específicas e também não sabem nada sobre outros elementos.

Em geral, se o projeto tiver algum tipo de identificação exclusiva global de todos os elementos da interface com seu ID, faz sentido organizar o nível de elementos da web como uma biblioteca global com solicitação de elementos específicos por seu ID através das classes de configuração factory ou \ xml na biblioteca spring. Mas nem em todos os projetos isso é possível.

Estrutura de teste


O conceito de desenvolvimento de autotestes, conforme mostrado no diagrama acima, é baseado na idéia de uma estrutura na qual um conjunto de funções do sistema é fornecido para todos os autotestes - eles são perfeitamente integrados e permitem que os desenvolvedores de autoteste se concentrem em questões específicas da implementação comercial das classes de teste.

A estrutura inclui os seguintes blocos funcionais:

  • Regras - inicialização e finalização dos componentes da infraestrutura de teste como inicialização do WebDriver e recebimento de um teste de vídeo (descrito em mais detalhes abaixo);
  • WebDriverHandlers - funções auxiliares para trabalhar com um driver da Web, como executar Java Script ou acessar logs do navegador. Implementado como um conjunto de métodos estáticos sem estado;
  • WebElements - uma biblioteca de elementos típicos da Web ou de seus grupos, contém a funcionalidade de função cruzada necessária e o comportamento típico. No nosso caso, essa funcionalidade inclui uma possível verificação da conclusão de operações assíncronas no lado do navegador da web. Implementado como extensões de elementos da Web das bibliotecas Selenium e Selenide.

Inicialização do ambiente de teste. Regras


A classe-chave para todas as classes de teste é BaseTest, da qual todas as classes de teste são herdadas. A classe BaseTest define o "corredor" de testes do Junit e o RuleChain usado, como mostrado abaixo. O acesso das classes de teste do aplicativo às funções fornecidas pelas classes de regra é realizado através dos métodos estáticos das classes de regra, usando o identificador de segmento "thread" como chave.

Detalhes da implementação de classes base para a estrutura de autoteste serão mostrados na próxima parte do artigo: consulte a Parte 2-1. Implementação de classe base para todos os testes e JUnit ChainRule.

Documentar resultados através de relatórios Allure.


Para relatórios detalhados sobre os testes realizados, é utilizado o Allure Framework, integrado ao Bamboo por meio do Allure Plugin . Isso fornece uma oportunidade para os consumidores de teste específicos (equipe de teste funcional) não apenas receberem dados sobre o fato de um teste específico falhar, mas também restaurar facilmente e, se necessário, repetir um teste eliminado no modo manual.

Para documentar o relatório de teste, é utilizada a seguinte funcionalidade:

  • Anotações Allure e Junit para marcar o relatório pelas etapas do script de teste, bem como uma descrição estática dos metadados para o teste;
  • Adquira anexos para anexar ao relatório informações adicionais como um teste de vídeo, captura de tela, resultados de diagnósticos adicionais dos motivos da falha, registros do navegador da Web baixados de / para o navegador de arquivos.

As seguintes anotações Allure e Junit são usadas para marcar o relatório.

  • No nível da classe de teste:

    o @ Feature - o nome do subsistema funcional ao qual o teste pertence;
    o @ Story - o nome de um caso de teste específico;
    o @ Proprietário - o nome do desenvolvedor que fez as últimas alterações no teste;
    o @ TmsLink - link para a descrição do teste no “sistema de gerenciamento de testes” usado.
  • No nível do método de teste (@ Test) da classe de teste:

    o @ DisplayName - nome completo do script de teste. Difere do @ Story, pois permite compartilhar os mesmos scripts, que diferem apenas na composição da cena de teste;
  • No nível dos métodos que correspondem às etapas específicas do cenário de teste:

    o @ Step - o nome significativo da etapa de teste que reflete a essência comercial do que está acontecendo.

A nomeação das etapas de teste através de @ Step permite criar um relatório detalhado que repete completamente todas as etapas e itens descritos no cenário de teste. Isso permite que os usuários dos autotestes rastreiem facilmente o curso do cenário de teste e ajudam a determinar o ponto de incidência.

Em geral, o uso do Allure Framework provou ser muito útil e fácil, com exceção de alguns recursos relacionados à enorme quantidade de dados gerados no nosso caso para um grande número de scripts de teste longos e complexos (descritos mais adiante na seção "Retrospectiva").

O que poderia ter sido feito de maneira diferente imediatamente? Use o Spring Framework


Apesar de, ao implementar autotestes, intencionalmente nos recusamos a usar o Spring Core, em geral, considero seu uso justificado e funcional. O protótipo implementado mostrou que o uso do Spring Core funciona para as seguintes tarefas (embora, é claro, não o tenhamos testado completamente):

  • inicialização da cena de teste;
  • inicialização de páginas e elementos da web.

O único recurso é a necessidade de usar o contexto de escopo "padrão" do nível do protótipo.

Inicialização da cena do teste. Nos autotestes, a cena de teste é inicializada pelo método clássico de criar instâncias de classe diretamente na classe de teste por meio de uma simples fábrica de objetos ou nova. Aqui é bastante razoável usar a inicialização por meio de classes de configuração ou por meio de arquivos xml ou de arquivo externos. As vantagens são as seguintes: isso 1) simplifica o código de revisão, pois as alterações na cena de teste não se aplicam mais às classes-chave; 2) permite conectar diferentes cenas de teste para diferentes suportes ou outras condições. Agora o ponto 2 não é usado conosco.

Inicialização de páginas e elementos da web. A injeção de classes de páginas da web e elementos da web funciona em virtude da inicialização lenta e preguiçosa dos elementos da web selenide.

Assim, torna-se possível criar uma biblioteca de elementos e páginas da web de acordo com uma determinada especificação global da interface do usuário e receber no código links para esses elementos não pelo caminho e ID absolutos, mas pelo ID do projeto de acordo com a especificação.

Devo dizer imediatamente que não testei isso com muito cuidado; na verdade, me limitei a uma implementação de teste das “páginas” do login e da página de “informações” do usuário sobre esse princípio.

Retrospectiva. Surpresas


Aqui descrevi as surpresas inesperadas que surgiram durante o desenvolvimento do projeto, além de algumas considerações sobre o que poderia ter sido melhor se não "."

Marcação de front-end amigável ao selênio (Angular)


Uma das surpresas mais sérias que encontramos foi o layout da página "flutuante", que levou ao fato de que, após a atualização dos aplicativos Angular, os testes caíram porque o Selenium não conseguiu encontrar os elementos porque seu ID (id, classe ou modelo ng) ou caminhos (para XPath). Isso levou à instabilidade dos testes e à ambiguidade das razões da queda.

Infelizmente, esse problema acabou sendo geralmente insolúvel. Nós o contornamos com medidas organizacionais: 1) no início do teste de um novo candidato à liberação, todos os desenvolvedores de autotestes se concentram na rápida eliminação e refinamento em termos de edição dos valores dos localizadores de elementos da web; 2) no final, passamos a usar o XPath relativo, o que, infelizmente, não melhora o desempenho.

– , - .

«download»


«» :


.

. , , . , .


. , «» . , , , , , - ( ) - .

, , , . .

, . «» , , , .


. , . (~ 1000 ) 6 .

Selenoid-ggr . junit 20 ( Selenoid-) 60 ( ), . «» , 60 .

, , , 3-4 , preQA-. . , , AWS c , Selenoid- , AWS Selenoid, in/out .
, , E2E . preQA.

«»


, 60 60 .

«Timeline» « » ( ). , .


Havia duas razões. O "plug" inicial foi causado pelo apelo em massa ao serviço de autorização e à conta pessoal. Todos os navegadores simultaneamente iniciam o login e sobrecarregam o serviço no lado da bancada de testes.

As "colunas" subseqüentes mostraram-se relacionadas ao fato de que as classes de teste estão localizadas em pastas por subsistema e junit as executaram quase simultaneamente, carregando assim um subsistema funcional específico do estande e indo além do limite de desempenho da configuração do estande.

Resolvemos o primeiro problema, agrupando o método de script de login no balanceador, que limitava o número máximo de operações de login a uma determinada constante.

@Step(":    ")     public void apply(User user) {     try {         LoadCounter.get(); //   .              try {                 applyLogin(user); //          } catch (Throwable t) {             throw t; //                   } finally {             LoadCounter.release(); //            }         } catch (LoadCounterException e) { //                  throw new StandRuntimeException("Can not get loadcounter for Login action.", e);     }     } 

O segundo problema foi resolvido ativando a opção de execução de teste aleatório, que o plugin junit maven possui.

    <plugin>               <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>         <version>….</version>            <configuration>                <!-- Defines the order the tests will be run in. Supported values are "alphabetical",                     "reversealphabetical", "random","hourly", "failedfirst", "balanced" and "filesystem". -->                <runOrder>random</runOrder>                … 

Deve-se notar que esses problemas estavam relacionados ao fato de não termos tido a oportunidade de aumentar o desempenho da bancada de testes devido a recursos limitados e também porque esses recursos ficariam ociosos a maior parte do tempo.

Em geral, verificou-se que o sistema E2E interno de autotestes da interface do usuário da Web pode muito bem ser usado para testes de carga substitutos e triagem do desempenho e da estabilidade do sistema testado como um todo.

Carga alta no servidor Bamboo e seu armazenamento


Se você tiver muitos testes com um grande número de operações (por exemplo, o número de operações registradas na Etapa é de 200 a 2000 com o número de testes em torno de 1300), o volume do relatório Allure se tornará mais do que significativo. Se você adicionar aqui também vários anexos, como capturas de tela e arquivos enviados / enviados, o volume já será de centenas de megabytes. Por exemplo, agora para uma regressão noturna de 900 testes, a quantidade de dados carregados apenas no Bamboo Allure é de cerca de 600 MB.

É claro que os engenheiros do DevOps não estão entusiasmados com a intensidade de consumir espaço em disco e expressam insatisfação ativamente, principalmente em relação à necessidade de armazenar dados por pelo menos um ano.

Vemos uma saída dessa situação usando um servidor externo para armazenar e processar relatórios, por exemplo, como o Allure Enterprise. Este servidor já está pago, mas nos permite resolver esse problema. Atualmente, estamos trabalhando no teste e na integração do Allure Enterprise em nosso projeto.

Para ser continuado


No próximo artigo, meus colegas continuarão a história e descreverão a fascinante história de teste de serviços da Web usando o Smartbear SoapUI Pro. As forças de dois engenheiros de automação conseguiram automatizar cerca de 500 scripts de teste; para esse fim, tive que implementar uma estrutura adicional que expanda significativamente a funcionalidade das funções padrão da interface do usuário SOAP, mas mais sobre isso posteriormente.

Este artigo foi escrito em colaboração com o gerente do projeto e o proprietário do produto kotalesssk .

Parte 1. Organizacional e gerencial. Por que precisamos de automação. Organização do processo de desenvolvimento e gerenciamento. Organização de uso
Parte 2. Técnica. Arquitetura e pilha técnica. Detalhes de implementação e surpresas técnicas
Parte 2-1. Implementação de classe base para todos os testes e JUnit ChainRule
Parte 2-2. Implementação do processo de upload de um arquivo de um contêiner com um navegador para uma estrutura de teste. Pesquise o nome do arquivo baixado pelo navegador

Source: https://habr.com/ru/post/pt477044/


All Articles