Criando uma ferramenta para escrever de forma rápida e eficiente autotestes no Selenium

Automação Fundamental de Blocos de Construção - Testes
Rod Johnson
imagem

Não sou um embaixador para testar interfaces da Web, mas é mais provável que este ensaio seja útil para camaradas que já têm experiência nesse campo.

Também será útil para iniciantes, pois Eu forneço o código-fonte onde você pode ver como a interação com o selênio é organizada no produto final.

Vou falar sobre como, a partir do zero, com pouca experiência em desenvolvimento, escrevi uma plataforma para a execução de testes e sobre a própria plataforma. Eu mesmo acredito que meu produto acabou sendo muito eficaz, o que significa que será útil para muitos e tem um lugar para consideração.

Do conceito


O processo de teste depende do sistema de informação.

Para lembrar do meu conceito, é necessário entender em quais sistemas eu me concentro - esses são sistemas em que geralmente existem processos de negócios lineares específicos que são definidos como a chave ao realizar testes de regressão.

Então, um sistema como srm. Uma entidade comercial importante é a oferta do fornecedor. A principal consideração na realização de testes de regressão é a integridade do processo de negócios.
O processo de negócios começa a partir do registro do fornecedor no sistema e, em seguida, prossegue a criação da proposta comercial - ele passa para o estágio de consideração, realizado por vários tipos de usuários internos (e cada usuário possui uma interface exclusiva) até a decisão de consideração da proposta ao fornecedor ser devolvida.

Acontece que passamos por várias interfaces diferentes e quase sempre trabalhamos com diferentes. De fato - se você olhar diretamente para ele - parece assistir a uma fita de vídeo - ou seja, este é um processo que tem um começo e um fim, e é absolutamente linear - sem ramificação, ao escrever um teste, sempre sabemos o resultado esperado. I.e. Quero dizer que, já olhando para essa foto, podemos concluir que é improvável que os testes polimórficos sejam bem-sucedidos. Em vista disso, ao criar uma plataforma para a execução de testes, o principal fator que defini a velocidade de execução dos testes.

Os conceitos que estabeleci para mim:

  1. O autoteste deve ser criado o mais rápido possível. Se você conseguir isso qualitativamente, outros aspectos, como confiabilidade e facilidade de uso, deverão surgir sozinhos.
  2. Os testes devem ser declarados declarativamente e viver separadamente do código. Eu nem vi outra opção. Isso aumenta a velocidade da escrita, pois se você tiver um intérprete pronto - nossa plataforma, não precisará adicionar nada mais tarde, não precisará entrar no código novamente - em geral, poderá esquecer a plataforma finalizada usando o IDE. Portanto, os testes são mais fáceis de manter. Nesta forma, eles são mais fáceis de aprender a escrever, porque habilidades de desenvolvimento não são necessárias, mas apenas uma compreensão da linguagem de marcação. Nesta forma, eles são entendidos por todos os participantes do processo.

O que eu decidi recusar no início:

  1. NÃO envolva seu sistema em uma estrutura de teste. Você pode iniciar a execução de um processo sem uma estrutura de teste. "Você quer inventar uma bicicleta!" - muitos dirão. Eu penso diferente. Estruturas de teste usadas populares foram criadas principalmente para testar o código de dentro e vamos testar a parte externa do sistema de fora. É como se eu tivesse uma bicicleta de estrada e precisasse descer a montanha fora da estrada (rude, mas a linha de raciocínio reflete). Em geral, nós mesmos escreveremos a estrutura - com blackjack e ... (embora eu saiba que, por exemplo, o JUnit 5 já esteja muito mais adaptado para essas tarefas).
  2. Recusa em usar invólucros para selênio. Na verdade, a própria biblioteca de chaves é pequena. Para entender que você precisa usar 5% de sua funcionalidade, enquanto a limpa completamente, levará várias horas. Pare de procurar em todos os lugares uma maneira de escrever menos código e se acostumar com o penico. No mundo moderno, esse desejo geralmente leva ao absurdo e quase sempre causa danos à flexibilidade (quero dizer as abordagens para "escrever menos código" e não os casos de estruturas arquitetônicas).
  3. Bela apresentação dos resultados não é necessária. Introduziu este item, porque Eu nunca fui confrontado com isso. Quando o autoteste é concluído, preciso saber duas coisas: o resultado geral (positivo / negativo) e se houve um erro - onde exatamente. Talvez você ainda precise manter as estatísticas. Tudo o resto em termos de resultados não é ABSOLUTAMENTE essencial. Considerar um belo design como uma vantagem significativa ou gastar tempo com esse belo design nos estágios iniciais - são exibições supérfluas.

Falarei um pouco mais sobre o nível de desenvolvimento na empresa e as condições para criar a ferramenta para esclarecer completamente alguns detalhes.

Devido a algumas circunstâncias confidenciais, não divulgo a empresa em que trabalho.

Em nossa empresa, o desenvolvimento já existe há muitos anos e, portanto, todos os processos já foram estabelecidos. No entanto, eles estão muito atrás das tendências atuais.
Todos os representantes de TI entendem que é necessário cobrir o código com testes, escrever scripts para autotestes no momento da coordenação de requisitos para funcionalidade futura, tecnologias flexíveis economizam significativamente tempo e recursos e o IC que simplesmente tira e simplifica a vida. Mas tudo isso até agora apenas lentamente nos chega ...

O mesmo ocorre com o serviço de controle de qualidade de software - todos os testes são executados manualmente, se você observar o processo "de cima" - este é o "gargalo" de todo o processo de desenvolvimento.

Descrição da montagem


A plataforma é escrita em Java usando JDK 12

Principais ferramentas de infraestrutura - Selenium Web Driver, OJDBC

Para que o aplicativo funcione, o navegador FireFox versão 52 deve estar instalado no PC

Composição de Compilação do Aplicativo


imagem

Com o aplicativo, são necessárias 3 pastas e 2 arquivos:

Pasta BuildKit - contém:

  • jdk12, através do qual o aplicativo é iniciado (JVM);
  • geckodriver.exe - para o Selenium Web Driver funcionar com o navegador FireFox;
  • SprintAutoTest.jar - diretamente a instância do aplicativo

• Pasta Relatórios - os relatórios são salvos após o aplicativo concluir o caso de teste. Ele também deve conter a pasta ErrorScreens, onde a captura de tela é salva em caso de erro

• Pasta TestSuite - pacotes da web, javascripts, um conjunto de casos de teste (o preenchimento desta pasta será discutido em detalhes separadamente)

• arquivo config.properties - contém uma configuração para conectar-se a um banco de dados Oracle e valores de expectativas explícitas para o WebDriverWait

• starter.bat - arquivo para iniciar o aplicativo (é possível iniciar automaticamente o aplicativo sem especificar manualmente o TestCase se você inserir o nome TestCase como parâmetro no final).

Breve descrição do aplicativo


O aplicativo pode ser iniciado com o parâmetro (nome TestCase) ou sem ele - nesse caso, você deve inserir o nome do caso de teste no console.

Um exemplo do conteúdo geral de um arquivo bat a ser executado sem um parâmetro : inicie o "iniciador de teste automático"% cd% \ BuildKit \ jdk-12 \ bin \ java.exe -Xmx768M -jar - ativar a visualização% cd% \ BuildKit \ SprintAutoTest.jar

No início geral do aplicativo, ele examina os arquivos xml localizados no diretório "\ TestSuite \ TestCase" (sem exibir o conteúdo das subpastas). Nesse caso, a validação primária dos arquivos xml para a correção da estrutura ocorre (ou seja, todas as tags do ponto de vista da marcação xml estão corretas) e os nomes indicados na tag "testCaseName" são obtidos, após o que o usuário é solicitado a inserir um dos nomes possíveis para o teste disponível casos. No caso de uma entrada incorreta, o sistema solicitará que você digite o nome novamente.

Depois que o nome TestCase é recebido, o modelo interno é construído, que é um monte de TestCase (script de teste) - WebPackage (armazenamento de elementos) na forma de objetos java. Após criar o modelo, o TestCase (o objeto executável do programa) é criado diretamente. No estágio de construção do TestCase, a validação secundária também ocorre - é verificado se todos os formulários especificados no TestCase estão no WebPackage associado e se todos os elementos especificados em ação estão no WebPackage nas páginas especificadas. (A estrutura do TestCase e WebPackage é descrita em detalhes abaixo)

Após a criação do TestCase, o script é executado diretamente

Algoritmo de operação de script (lógica de chave)


TestCase é uma coleção de entidades de Ação, que por sua vez é uma coleção de entidades de Eventos.

Testcase
-> Lista {Ação}
-> Lista {Evento}

Quando o TestCase é iniciado, a Ação inicia seqüencialmente (cada Ação retorna um resultado lógico)

Quando a Ação inicia, o Evento inicia seqüencialmente (cada Evento retorna um resultado lógico)

O resultado de cada evento é salvo.

Assim, o teste é concluído quando todas as ações foram concluídas com êxito ou se Action retornou false.

* Mecanismo de colisão

Porque meu sistema em teste é antigo e detectou erros / erros que não são erros e alguns eventos não funcionam na primeira vez, a plataforma possui um mecanismo que pode se afastar do conceito acima de um teste estritamente linear (no entanto, é fortemente tipado). Ao capturar esses erros, é possível repetir os casos primeiro e executar ações adicionais para poder repetir ações.

No final do aplicativo, um relatório é gerado, salvo no diretório "\ Reports". Em caso de erro, uma captura de tela é salva, salva em "\ Reports \ ErrorScreens"

Preenchimento do TestSuite


Então, a descrição do teste. Como já mencionado, o principal parâmetro necessário para executar é o nome do teste a ser executado. Este nome é armazenado no arquivo xml no diretório “/ TestSuite / TestCase”. Todos os scripts de teste são armazenados neste diretório. Pode haver qualquer número deles. O nome do caso de teste é obtido não do nome do arquivo, mas da tag "testCaseName" dentro do arquivo.

O TestCase define o que exatamente será feito - ou seja, ação. No diretório “/ TestSuite / WebPackage” nos arquivos xml, todos os localizadores são armazenados. I.e. tudo nas melhores tradições - as ações são armazenadas separadamente, localizadores de formulários da web separadamente.

O TestCase também armazena o nome do WebPackage na tag "webPackageName".

A imagem total já está lá. Para executar, você deve ter 2 arquivos xml: TestCase e WebPackage. Eles formam um monte. WebPackage é independente - o identificador é o nome na tag "webPackageName". Portanto, aqui está a primeira regra - os nomes TestCase e WebPackage devem ser exclusivos. I.e. mais uma vez - na verdade, nosso teste é um monte de arquivos TestCase e WepPackage, que são conectados pelo nome do WebPackage, especificado no TestCase. Na prática, automatizo um sistema e uno todos os meus casos de teste a um WebPackage no qual tenho um monte de descrições de todos os formulários.

A próxima camada de decomposição lógica é baseada em um padrão como o Objeto de Página.

Objeto de Página
O objeto de página é uma das soluções de arquitetura mais úteis e usadas em automação. Esse padrão de design ajuda a encapsular o trabalho com elementos de página individuais. O Objeto de Página, por assim dizer, simula as páginas do aplicativo em teste como objetos.

Separação de lógica e implementação

Há uma grande diferença entre a lógica de teste (o que verificar) e sua implementação (como verificar). Um exemplo de cenário de teste: "O usuário digita um nome de usuário ou senha incorreto, pressiona o botão de login, recebe uma mensagem de erro." Este script descreve a lógica do teste, enquanto a implementação inclui ações como procurar campos de entrada na página, preenchê-los, verificar erros, etc. E se, por exemplo, o método de exibição de uma mensagem de erro for alterado, isso não afetará o script de teste, você também precisará inserir dados incorretos, pressionar o botão de login e verificar o erro. Mas isso afetará diretamente a implementação do teste - será necessário alterar o método de recebimento e processamento da mensagem de erro. Ao separar a lógica do teste da sua implementação, os autotestes se tornam mais flexíveis e, como regra, mais fáceis de manter.

*! Não se pode dizer que essa abordagem arquitetônica tenha sido totalmente aplicada. É apenas uma questão de decompor a descrição do script de teste página por página, o que ajuda a escrever testes mais rapidamente e adicionar verificações automáticas adicionais em todas as páginas, estimula a descrição correta dos localizadores (para que eles não sejam os mesmos em páginas diferentes) e cria uma estrutura lógica "bonita" do teste. A plataforma em si é implementada nos princípios de "Arquitetura Limpa"

Em seguida, tentarei não detalhar a estrutura do WebPackage e TestCase. Para eles, criei um esquema DTD para WebPackage e XSD 1.1 para TestCase.

! IMPORTANTE


Ao manter os esquemas DTD e XSD, o conceito de gravação rápida de teste é implementado.

Ao escrever o WebPackage e o TestCase diretamente, você deve usar o Editor xml com funções de validação DTD e XSD embutidas em tempo real com geração automática de tags, o que tornará o processo de gravação de um autoteste amplamente automatizado (todas as tags necessárias serão substituídas automaticamente, as listas suspensas serão exibidas para valores de atributos possíveis valores, de acordo com o tipo de evento, que as tags correspondentes serão geradas) .

Quando esses esquemas são "parafusados" ao próprio arquivo xml, você pode esquecer a correção da estrutura do arquivo xml, se você usar um ambiente especial. Minha escolha recaiu no oXygen XLM Editor. Mais uma vez - sem usar esse programa, você não entenderá a essência da velocidade de gravação. A idéia não é muito adequada para isso. ele não processa a construção "alternativa" do XSD 1.1, que é a chave do TestCase.

Webpackage


WebPackaege - um arquivo xml que descreve os elementos de formulários da web, localizados no diretório "\ TestSuite \ WebPackage". (pode haver quantos arquivos você quiser. O nome dos arquivos pode ser qualquer coisa - apenas o conteúdo é importante).

DTD (inserido no início do documento):
<!DOCTYPE webPackage [ <!ELEMENT webPackage (webPackageName, forms)> <!ELEMENT webPackageName (#PCDATA)> <!ELEMENT forms (form+)> <!ELEMENT form (formName, elements+)> <!ELEMENT formName (#PCDATA)> <!ELEMENT elements (element+)> <!ELEMENT element (name, locator)> <!ATTLIST element type (0|1|2|3|4|5|6|7) #REQUIRED> <!ATTLIST element alwaysVisible (0|1) #IMPLIED> <!ELEMENT name (#PCDATA)> <!ELEMENT locator (#PCDATA)> <!ATTLIST locator type (1|2) #IMPLIED> ]> 


Em geral, parece aproximadamente
 <webPackage> <webPackageName>_</webPackageName> <forms> <form> <formName>______</formName> <elements> <element type="2" alwaysVisible="1"> <name>_</name> <locator type="2">.//div/form/div/div/form/table/tbody/tr/td[text()=""]/following-sibling::td/input</locator> </element> <element type="2"> <name>__</name> <locator>.//div/form/div/div/form/table/tbody/tr/td[text()=""]/following-sibling::td/input</locator> </element> ....... </elements> </form> ....... </forms> </webPackage> 


Como já mencionado, para que os elementos não estejam em uma pilha - tudo é decomposto de acordo com os formulários da web

A entidade chave é
 <element> 

A tag do elemento possui 2 atributos:

  • tipo
  • alwaysVisible

O atributo type é obrigatório e especifica o tipo de elemento. Na plataforma, defina o tipo de byte

No momento, especificamente para ele próprio na plataforma, ele implementou os seguintes tipos:

• 0 - não tem um significado funcional, geralmente algum tipo de inscrição
• 1 botão (botão)
• 2 - campo de entrada
• 3 - caixa de seleção (caixa de seleção)
• 4 - lista suspensa (selecione) - não foi realmente implementado, mas deixou um local para ele
• 5 - para a lista suspensa srm: escreva o nome, aguarde o valor aparecer - selecione de acordo com o modelo xpath específico - o tipo específico para o meu sistema
• 6 - srm select - usado em funções típicas como pesquisa, etc. - digite especificamente para o meu sistema

O atributo AlwaysVisible - opcional - mostra se um elemento está sempre presente no formulário, pode ser usado durante a validação inicial / final de Action (ou seja, no modo automático, você pode verificar se, ao abrir o formulário, ele contém todos os elementos que estão sempre nele, ao fechar todos esses elementos desapareceram)

Valores possíveis:

  • 0 - por padrão (ou se o atributo não estiver definido) - o elemento pode não estar na página (não valida)
  • 1 - o elemento está sempre presente na página

Um atributo de tipo opcional opcional é implementado com a tag localizador

Valores possíveis:

  • 1 - procure um elemento pelo ID (respectivamente, especifique apenas o ID no localizador)
  • 2 - por padrão (ou se o atributo não estiver definido) - pesquise no xpath - é recomendável usar apenas a pesquisa no xpath, porque Este método combina quase todas as vantagens do resto e é universal

Testcase


TestCase - o arquivo xml que descreve diretamente o script de teste está localizado no diretório "\ TestSuite \ TestCase" (pode haver quantos arquivos você desejar. O nome dos arquivos pode ser qualquer coisa - apenas o conteúdo é importante).

Circuito XSD:
 <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.1"> <xs:element name="testCase"> <xs:complexType> <xs:sequence> <xs:element name="testCaseName" type="xs:string"/> <xs:element name="webPackageName" type="xs:string"/> <xs:element name="actions" type="actionsType"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="actionsType"> <xs:sequence> <xs:element name="action" type="actionType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="actionType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="orderNumber" type="xs:positiveInteger"/> <xs:element name="runConfiguration" type="runConfigurationType"/> </xs:sequence> </xs:complexType> <xs:complexType name="runConfigurationType"> <xs:sequence> <xs:element name="formName" type="xs:string"/> <xs:element name="repeatsOnError" type="xs:positiveInteger" minOccurs="0"/> <xs:element name="events" type="eventsType"/> <xs:element name="exceptionBlock" type="eventsType" minOccurs="0"/> </xs:sequence> <xs:attribute name="openValidation" use="required"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="closeValidation" use="required"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:complexType name="eventBaseType"> <xs:sequence> <xs:element name="orderNumber" type="xs:positiveInteger"/> </xs:sequence> <xs:attribute name="type" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="goToURL"/> <xs:enumeration value="checkElementsVisibility"/> <xs:enumeration value="checkElementsInVisibility"/> <xs:enumeration value="fillingFields"/> <xs:enumeration value="clickElement"/> <xs:enumeration value="dbUpdate"/> <xs:enumeration value="wait"/> <xs:enumeration value="scrollDown"/> <xs:enumeration value="userInput"/> <xs:enumeration value="checkInputValues"/> <xs:enumeration value="checkQueryResultWithUtilityValue"/> <xs:enumeration value="checkFieldsPresenceByQueryResult"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="invertResult" use="optional" default="0"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="hasExceptionBlock" use="optional" default="0"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:complexType name="eventsType"> <xs:sequence> <xs:element name="event" type="eventBaseType" maxOccurs="unbounded"> <xs:alternative test="@type='goToURL'" type="eventGoToURL"/> <xs:alternative test="@type='checkElementsVisibility'" type="eventCheckElementsVisibility"/> <xs:alternative test="@type='checkElementsInVisibility'" type="eventCheckElementsVisibility"/> <xs:alternative test="@type='fillingFields'" type="eventFillingFields"/> <xs:alternative test="@type='checkInputValues'" type="eventFillingFields"/> <xs:alternative test="@type='clickElement'" type="eventClickElement"/> <xs:alternative test="@TYPE='dbUpdate'" type="eventRequest"/> <xs:alternative test="@type='wait'" type="utilityValueInteger"/> <xs:alternative test="@type='scrollDown'" type="eventClickElement"/> <xs:alternative test="@type='userInput'" type="eventClickElement"/> <xs:alternative test="@type='checkQueryResultWithUtilityValue'" type="eventRequestWithValue"/> <xs:alternative test="@type='checkFieldsPresenceByQueryResult'" type="eventRequestWithValue"/> </xs:element> </xs:sequence> </xs:complexType> <!--   EVENTS --> <xs:complexType name="eventGoToURL"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="url" type="xs:anyURI"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventCheckElementsVisibility"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="fields" type="fieldType"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventFillingFields"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="fields" type="fieldTypeWithValue"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventClickElement"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="elementName" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventRequest"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="dbRequest" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="utilityValueInteger"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="utilityValue" type="xs:positiveInteger"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventRequestWithValue"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="dbRequest" type="xs:string"/> <xs:element name="utilityValue" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <!--   EVENTS --> <xs:complexType name="fieldType"> <xs:sequence> <xs:element name="field" maxOccurs="unbounded"> <xs:complexType> <xs:choice> <xs:element name="element" type="xs:string"/> <xs:element name="xpath" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="fieldTypeWithValue"> <xs:sequence> <xs:element name="field" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="element" type="xs:string"/> <xs:element name="value" type="valueType"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="valueType"> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="type" use="optional" default="1"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="2"/> <xs:enumeration value="3"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema> 


Vista geral:
 <!DOCTYPE testCase SYSTEM "./TestSuite/TestCase/entities.dtd" []> <testCase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="testShema.xsd"> <testCaseName>__testCase</testCaseName> <webPackageName>_webPackage__webPackageName</webPackageName> <actions> <action> <name>          </name> <orderNumber>10</orderNumber> <runConfiguration openValidation="1" closeValidation="1"> <formName>______</formName> <events> <event type="goToURL"> <orderNumber>10</orderNumber> <url>&srmURL;</url> </event> ....... </events> </runConfiguration> </action> ....... </actions> </testCase> 


Aqui nesta linha, você pode ver como fixar o esquema xsd para que o XML Editor o veja:

 <testCase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="testShema.xsd"> 

No TestCase, também uso entidades DTD que são armazenadas separadamente no mesmo diretório - um arquivo com a extensão .dtd. Nele, eu guardo quase todos os dados - constantes. Também construí a lógica de tal maneira que, para iniciar um novo teste, e ao longo do teste, novas entidades únicas foram criadas, uma nova espaçonave foi registrada, foi suficiente alterar 1 dígito nesse arquivo.

Sua estrutura é muito simples - vou dar um exemplo:
 <?xml version="1.0" encoding="UTF-8"?> <!ENTITY mainNumber '040'> <!ENTITY mail '@mail.ru'> <!ENTITY srmURL 'https://srm-test.ru'> 


Essas constantes são inseridas no valor da tag da seguinte maneira:

 <url>&srmURL;</url> 

- pode ser combinado.

! Recomendação - ao escrever testCase, você deve especificar essas entidades DTD dentro do documento e, depois que tudo funcionar de maneira estável, transferi-lo para um arquivo separado. Meu editor xml tem dificuldades com isso - ele não consegue encontrar o DTD e não leva em consideração o XSD, por isso recomendo

testCase

testCase - a tag mais pai contém:

  • testCaseName - o nome do nosso caso de teste, esse parâmetro é passado para a entrada do aplicativo
  • webPackageName - o nome do WebPackage, especificado em webPackageName (consulte o parágrafo acima no WebPackage)
  • ações - contêiner da entidade de ação

ação

Contém:

  • nome - nome - é recomendável especificar o nome do formulário e as principais ações - o que e por quê
  • orderNumber - número de série - parâmetro necessário para a ação de classificação (introduzido devido ao fato de que ao analisar xml em java usando determinadas ferramentas, a análise pode ser realizada em um ambiente multithread, para que a ordem possa prosseguir) - ao especificar a próxima ação, você pode pular - ou seja, ao classificar, importa apenas "mais / menos" e, portanto, é possível executar uma ação entre as já descritas, sem a necessidade de alterar a numeração inteira
  • runConfiguration - a descrição real do que acontecerá como parte da ação

runConfiguration

Contém:

  • atributo openValidation - opcional, o padrão é "0"
    • 0 - não conduz a validação inicial do formulário
    • 1 - validação inicial do formulário
  • atributo closeValidation - opcional, o padrão é "0"
    • 0 - não realiza validação do formulário final
    • 1 - validação do formulário final
  • formName - o nome do formulário no qual as ações serão executadas - o valor formName do WebPackage
  • repeatsOnError - opcional, indica quantas repetições devem ser executadas em caso de falha
  • events - contêiner da entidade do evento
  • exceptionBlock - opcional - um contêiner de entidades de eventos que são executadas em caso de erro

evento

Unidade estrutural mínima - esta entidade mostra quais ações são executadas

Cada evento é especial, pode ter tags e atributos exclusivos.

O tipo base contém:

  • atributo type - indica o tipo de elemento
  • o atributo hasExceptionBlock é um atributo opcional; por padrão, “0” é necessário para implementar o mecanismo de falha - o atributo indica que podemos esperar um erro neste evento
    • 0 - nenhum erro esperado
    • 1 - um possível erro é esperado na ação
  • atributo invertResult - atributo opcional, o padrão é "0" - o atributo indica que é necessário alterar o resultado do evento
    • 0 - deixe o resultado do evento
    • 1 - mude o resultado do evento para o oposto

*! Um mecanismo para descrever o erro esperado
Deixe-me dar um exemplo trivial de onde o usei pela primeira vez e o que fazer para fazê-lo funcionar.

Caso: entrada Captcha. No momento, eu não conseguia automatizar, por assim dizer, uma verificação para o robô travar - eles não me escrevem um serviço de teste captcha (mas é mais fácil criar uma rede neural para reconhecimento))) Portanto, podemos cometer um erro ao entrar. Nesse caso, eu faço um evento de controle no qual verifico se não temos um elemento - uma notificação sobre um código de controle incorreto, coloquei o atributo hasExceptionBlock nele. Anteriormente, pedi à ação que pudéssemos ter várias repetições (5) e, afinal, registrei o bloco de exceção, no qual escrevi que tinha que pressionar o botão de saída para a notificação e a ação era repetida.

Exemplos do meu contexto.

Aqui está como eu registrei o evento:

 <event type="checkElementsInVisibility" hasExceptionBlock="1"> <orderNumber>57</orderNumber> <fields> <field> <element>___</element> </field> </fields> </event> 

E aqui exceptionBlock após eventos

  <exceptionBlock> <event type="clickElement"> <orderNumber>10</orderNumber> <elementName>_____</elementName> </event> </exceptionBlock> 

E sim, as ações em uma página podem ser decompostas em várias ações.

+ que notaram 2 parâmetros na configuração: defaultTimeOutsForWebDriverWait, lowTimeOutsForWebDriverWait. Então é por isso que eles são. Porque Eu tenho o driver da web inteiro em um singleton e não queria criar um novo WebDriverWait todas as vezes, então eu tenho 1 rápido e isso ocorre em caso de erro (bem, ou se você colocar hasExceptionBlock = "1", será estúpido com menos tempo espera explícita) - bem, você deve concordar, aguarde um minuto para garantir que a mensagem não saia como faut, além de criar um novo WebDriverWait a cada vez. Bem, essa situação em que lado não gruda exige muleta - decidi fazê-lo.

Tipos de eventos


Aqui darei um conjunto mínimo de meus eventos, como um conjunto de reconhecimento, com o qual posso testar quase tudo no meu sistema.

E agora é fácil para o código entender o que é um evento e como ele é construído. O código implementa essencialmente a estrutura. Eu tenho 2 classes - DataBaseWrapper e SeleniumWrapper. Nessas classes, a interação com os componentes da infraestrutura é descrita e os recursos da plataforma também são refletidos. Vou dar a interface que implementa o SeleniumWrapper

 package logic.selenium; import models.ElementWithStringValue; import models.webpackage.Element; import org.openqa.selenium.WebElement; public interface SeleniumService { void initialization(boolean webDriverWait); void nacigateTo(String url); void refreshPage(); boolean checkElementNotPresent(Element element); WebElement findSingleVisibleElement(Element element); WebElement findSingleElementInDOM(Element element); void enterSingleValuesToWebField(ElementWithStringValue element); void click(Element element); String getInputValue(Element element); Object jsReturnsValue(String jsFunction); //Actions actions void doubleClick(Element element); void moveMouseToElement(Element element); void pressKey(CharSequence charSequence); void getScreenShot(String storage); } 

Ele descreve todos os recursos dos chips de plataforma de selênio e de sobreposição - bem, na verdade o chip principal é o método "enterSingleValuesToWebField". Lembre-se de que no WebPackage especificamos o tipo de elemento. Então, como reagir a esse tipo ao preencher os campos está escrito aqui. Escrevemos 1 vez e esquecemos. O método acima deve ser corrigido por si mesmo em primeiro lugar. Por exemplo, os tipos 5 e 6, atualmente em vigor, são adequados apenas para o meu sistema. E se você tem um filtro e precisa filtrar muito, é típico (no aplicativo da Web), mas para usá-lo, você deve primeiro mover o mouse sobre o campo, aguardar a exibição de alguns campos, alternar para outros, aguardar existe algo, então vá lá e entre ... Estupidamente prescreva o mecanismo de ação uma vez, dê um tipo único a tudo isso na estrutura do switch - não se preocupe - obtenha um método polimórfico para todos os filtros de aplicativos semelhantes.

Portanto, no pacote “package logic.testcase.events” existe uma classe abstrata que descreve as ações gerais do evento. Para criar seu próprio evento exclusivo, você precisa criar uma nova classe, herdar dessa classe abstrata e já possui dataBaseService e seleniumService no kit - e depois determinar quais dados você precisa e o que fazer com eles. Algo assim. Bem e, consequentemente, após a criação de um novo evento, você precisa concluir a classe de fábrica TestCaseActionFactory e, se possível, o esquema XSD. Bem, se um novo atributo for adicionado - modifique o próprio modelo. De fato, é muito fácil e rápido.

Então, um batedor.

goToURL - geralmente a primeira ação - clique no link especificado

Um exemplo:
 <event type="goToURL"> <orderNumber>10</orderNumber> <url>testURL</url> </event> 


fillFields - Preenchendo elementos especificados

Tags especiais:

  • campos - fiel do contêiner da entidade
    • campo - contém a tag do elemento
      • elemento - indica o nome do elemento do webPackage
      • valor - qual valor indicar, possui um atributo de tipo opcional (se o elemento for uma caixa de seleção, um dos valores será indicado: "marcar" ou "desmarcar")

  • atributo type - indica como obter o valor, opcional, o valor padrão é "1"
    • 1 - o valor especificado é obtido
    • 2 - neste caso, a função JS especificada no diretório "\ TestSuite \ JS" é executada! IMPORTANTE - o nome do arquivo txt é indicado, sem ".txt" (e até agora encontrei aplicativos para funções js até agora apenas neste formulário - eu o uso em um único local para gerar uma pousada aleatória, mas o espectro de aplicativos possíveis é amplo)
    • 3 - neste caso, a consulta no banco de dados é indicada como o valor, e o programa substitui o primeiro resultado dessa consulta

Um exemplo:
 <event type="fillingFields"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> <value>test</value> </field> </fields> </event> 


checkElementsVisibility - verifica se os elementos especificados estão presentes no formulário (a saber, visíveis e não apenas no DOM). No atributo de campo, um elemento do WebPackage ou diretamente xpath pode ser especificado

Um exemplo:
 <event type="checkElementsVisibility"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> </field> <field> <xpath>test</xpath> </field> </fields> </event> 


checkElementsInVisibility - semelhante ao checkElementsVisibility, mas vice-versa

clickElement - clique no elemento especificado

Um exemplo:
 <event type="clickElement"> <orderNumber>10</orderNumber> <elementName>test</elementName> </event> 


checkInputValues - verifica os valores inseridos

Um exemplo:
 <event type="checkInputValues"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> <value>test</value> </field> </fields> </event> 


dbUpdate - faça uma atualização no banco de dados (o oxygen reage estranhamente a 1 evento do tipo dbUpdate - não sei o que fazer com ele e não entendo o porquê )

Um exemplo:
 <event type="dbUpdate"> <orderNumber>10</orderNumber> <dbRequest>update - </dbRequest> </event> 


CheckQueryResultWithUtilityValue - verifique o valor digitado pelo usuário com o valor do banco de dados

Um exemplo:
 <event type="checkQueryResultWithUtilityValue"> <orderNumber>10</orderNumber> <dbRequest>select ...</dbRequest> <utilityValue>test</utilityValue> </event> 


checkFieldsPresenceByQueryResult - verifique a presença de elementos no formulário por xpath por padrão. Se o padrão desejado não for especificado, a pesquisa ocorrerá de acordo com o padrão .//* [text () [contains (normalize-space (.), "$")]]], Onde em vez de "$" haverá um valor no banco de dados. Ao descrever seu próprio padrão, você deve indicar "$" no local em que deseja colocar o valor no banco de dados. No meu sistema, existem as chamadas grades nas quais existem valores que geralmente são formados a partir de algum tipo de visão. Este evento para testar essas grades

Um exemplo:
 <event type="checkFieldsPresenceByQueryResult"> <orderNumber>10</orderNumber> <dbRequest>test</dbRequest> <utilityValue></utilityValue> </event> 


Aguarde - tudo é simples - aguardando o número especificado de milissegundos. Infelizmente, mesmo que isso seja considerado uma muleta, direi com certeza - às vezes é impossível ficar sem ela

Um exemplo:
 <event type="wait"> <orderNumber>10</orderNumber> <utilityValue>1000</utilityValue> </event> 


scrollDown - role para baixo a partir do elemento especificado. Isso é feito da seguinte maneira: clica no elemento especificado e pressiona a tecla “PgDn”. Nos meus casos em que tive que rolar para baixo, funciona bem:

Um exemplo:
 <event type="scrollDown"> <orderNumber>10</orderNumber> <elementName>test</elementName> </event> 


userInput - insira um valor no elemento especificado. O único dispositivo semi-automático na minha automação, usado apenas para captcha. O elemento para inserir o valor é indicado. O valor é inserido na caixa de diálogo pop-up.

Um exemplo:
 <event type="userInput"> <orderNumber>10</orderNumber> <elementName>capch_input</elementName> </event> 


Sobre o código


Então, tentei fazer a plataforma de acordo com os princípios da arquitetura limpa do tio Bob.

Pacotes:

aplicativo - inicialização e inicialização + configurações e relatório (não me repreenda pela classe Report - é isso que o torna o mais rápido possível e o mais rápido possível)

logic - a lógica principal + serviços do Selenium e DB. Há eventos.

models - POJO em XML e todas as classes de objetos auxiliares

utils - singleton para selênio e db

Para executar o código, você precisa fazer o download do jdk 12 e especificar em qualquer lugar para que seus chips sejam ativados. No Idea, isso é feito através da Estrutura do projeto -> Módulos e Projeto. Também não se esqueça do corredor Maven. --enable-preview. .

, , JDK ojdbc “SprintAutoTest\src\lib”. , .. – , , ( , , )

Sumário


, , . 1,5 , 5 – 6 . 3700 830 ( 4800 ). , , , , . – -, - , -, , , ( – closeValidation . , , , action, ).

, xml, , (.. 2 – 1- xml , 2- TestCase).

– . , , . :

:

  • + — « » + — ( – .. -)
  • ( action — event c + )
  • , , – -. , – . , . , – ,
  • unit ( true false)

-, , , Cucumber BDD ( ):

  • . I.e. , «When», , .
  • . ( , ). , Given When – 99% , – , , .

, , :

  • ,
  • , , ,
  • . git, , , , ,
  • , , 1 , –
  • , , - user friendly
  • selenium server. , , CI, Team City .

- . github: .

, .

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


All Articles