Mudanças entre as edições de "Framework de testes automatizados"
(→Cenários decorados) |
m (Protegeu "Framework de testes automatizados" ([edit=sysop] (tempo indefinido) [move=sysop] (tempo indefinido))) |
||
(110 edições intermediárias de 2 usuários não apresentadas) | |||
Linha 5: | Linha 5: | ||
O objetivo de se desenvolver um ''framework'' para automação dos testes é permitir que seja criada uma camada de dados independente da camada de algoritmos que executam a automação. Esta é a tradicional divisão de software em duas camadas. Esta divisão (algoritmo ''versus'' dados) é válida tanto para testes automatizados funcionais como para testes unitários. | O objetivo de se desenvolver um ''framework'' para automação dos testes é permitir que seja criada uma camada de dados independente da camada de algoritmos que executam a automação. Esta é a tradicional divisão de software em duas camadas. Esta divisão (algoritmo ''versus'' dados) é válida tanto para testes automatizados funcionais como para testes unitários. | ||
− | Este conteúdo descreve a documentação técnica para manutenção do ''framework'' de testes automatizados a ser utilizado para testes unitários e testes funcionais. O documento está organizado em seções que descrevem separadamente as características de cada parte da arquitetura proposta. Para facilitar a identificação das classes dos núcleos | + | Este conteúdo descreve a documentação técnica para manutenção do ''framework'' de testes automatizados a ser utilizado para testes unitários e testes funcionais. O documento está organizado em seções que descrevem separadamente as características de cada parte da arquitetura proposta. Para facilitar a identificação das classes dos núcleos, elas estão coloridas de acordo com o núcleo da qual fazem parte, conforme ilustrado pela legenda a seguir. |
− | [[Arquivo:Legenda.png]] | + | [[Arquivo:Legenda.png|250px]] |
+ | |||
+ | Caso o interesse do leitor seja na documentação sobre como utilizar o ''framework'' para automação de testes, ela pode ser obtida [http://www.cnj.jus.br/wikipje/index.php/Testes_automatizados_funcionais_-_passo_a_passo neste link]. | ||
== '''VISÃO ARQUITETURAL POR SERVIÇO PRESTADO''' == | == '''VISÃO ARQUITETURAL POR SERVIÇO PRESTADO''' == | ||
Linha 13: | Linha 15: | ||
A arquitetura do ''framework'' desenvolvido pode ser vista sob o ponto de vista da funcionalidade fornecida para os desenvolvedores, conforme ilustrado na figura abaixo. | A arquitetura do ''framework'' desenvolvido pode ser vista sob o ponto de vista da funcionalidade fornecida para os desenvolvedores, conforme ilustrado na figura abaixo. | ||
− | [[Arquivo:Arquitetura_funcional.png| | + | [[Arquivo:Arquitetura_funcional.png|180px]] |
=== Núcleo genérico === | === Núcleo genérico === | ||
− | O núcleo genérico do ''framework'' tem a função de carregar os dados dos cenários (provenientes de arquivos XML), carregar os cenários e executá-los, independentemente se esta execução | + | O núcleo genérico do ''framework'' tem a função de carregar os dados dos cenários (provenientes de arquivos XML), carregar os cenários e executá-los, independentemente se esta execução for um teste funcional com Selenium ou de um teste unitário. Trata-se de um conjunto de classes, em sua maioria abstratas, que definem comportamentos esperados da funcionalidade de testes automatizados. |
=== Núcleo Selenium === | === Núcleo Selenium === | ||
− | O núcleo Selenium é uma camada sobre o núcleo genérico, que usa os serviços deste (por herança e composição) para a execução de testes funcionais em páginas web. É um conjunto de classes em sua maioria também abstratas que, além de estender as classes do núcleo genérico, faz uso do Selenium WebDriver para acessar o navegador, preencher os campos de formulário e conferir os valores dos elementos HTML | + | O núcleo Selenium é uma camada sobre o núcleo genérico, que usa os serviços deste (por herança e composição) para a execução de testes funcionais em páginas web. É um conjunto de classes, em sua maioria também abstratas que, além de estender as classes do núcleo genérico, faz uso do Selenium WebDriver para acessar o navegador, preencher os campos de formulário e conferir os valores dos elementos HTML. |
− | + | ||
− | + | ||
− | + | É importante observar que esta camada é independente do sistema web alvo do teste. Ou seja, qualquer sistema web com interface HTML/XHTML pode ter seus testes funcionais automatizados com base no ''framework'' desenvolvido. | |
− | + | === Núcleo PJe === | |
− | + | Nesta camada reside uma infraestrutura pra facilitar a automação dos casos de teste do Pje. Elas são desenvolvidas segundo o padrão [https://code.google.com/p/selenium/wiki/PageObjects Page Object] e, na hierarquia do ''framework'', estendem funcionalidades de '''FluentPage'''. Trata-se de um conjunto de classes abstratas que implementam alguns dos métodos exigidos pelas demais camadas do ''framework''. | |
== '''VISÃO ARQUITETURAL POR CAMADAS''' == | == '''VISÃO ARQUITETURAL POR CAMADAS''' == | ||
Linha 35: | Linha 35: | ||
Sob outro ponto de vista o ''framework'' pode ser analisado de acordo com a divisão tradicional da arquitetura em camadas, com divisão de dados e lógica de negócio. A Figura a seguir exibe a arquitetura dividida em camadas. | Sob outro ponto de vista o ''framework'' pode ser analisado de acordo com a divisão tradicional da arquitetura em camadas, com divisão de dados e lógica de negócio. A Figura a seguir exibe a arquitetura dividida em camadas. | ||
− | [[Arquivo:Visão em camadas.png|400px]] | + | [[Arquivo:Visão em camadas.png|400px|Figura 1]] |
=== Arquivo xml cenários === | === Arquivo xml cenários === | ||
Linha 45: | Linha 45: | ||
Este arquivo funciona como um conjunto de links para os cenários que serão executados, cenários estes cujos dados de execução são definidos no arquivo xml descrito na seção anterior. | Este arquivo funciona como um conjunto de links para os cenários que serão executados, cenários estes cujos dados de execução são definidos no arquivo xml descrito na seção anterior. | ||
− | De acordo com a relação entre o arquivo indexador e o arquivo de cenários ilustrada na Figura acima, um arquivo indexador fornece links para vários arquivos de cenários | + | De acordo com a relação entre o arquivo indexador e o arquivo de cenários ilustrada na Figura acima, um arquivo indexador fornece links para vários arquivos de cenários. |
Em geral deve haver poucos arquivos de indexação de cenários, pois cada um deles referencia inúmeros arquivos de cenários. | Em geral deve haver poucos arquivos de indexação de cenários, pois cada um deles referencia inúmeros arquivos de cenários. | ||
Linha 77: | Linha 77: | ||
A figura abaixo apresenta um diagrama de sequência que ilustra a dinâmica de comunicação entre as classes do ''framework''. Trata-se apenas de uma visão geral, e não da real implementação desta lógica. | A figura abaixo apresenta um diagrama de sequência que ilustra a dinâmica de comunicação entre as classes do ''framework''. Trata-se apenas de uma visão geral, e não da real implementação desta lógica. | ||
− | [[Arquivo: | + | [[Arquivo:Sequência carga cenarios resumido.png|800px]] |
A sequência descrita no diagrama é executada para cada uma das classes que herdam da superclasse '''TesteBase'''. A seguir é apresentada uma breve descrição dos passos ilustrados no diagrama: | A sequência descrita no diagrama é executada para cada uma das classes que herdam da superclasse '''TesteBase'''. A seguir é apresentada uma breve descrição dos passos ilustrados no diagrama: | ||
Linha 95: | Linha 95: | ||
== '''ESTRUTURA DO NÚCLEO GENÉRICO''' == | == '''ESTRUTURA DO NÚCLEO GENÉRICO''' == | ||
− | O núcleo genérico é composto basicamente por classes responsáveis por carregar dados de arquivos XML e executar cenários de teste. Apesar de alguns dados serem carregados automaticamente pelo ''framework'' a partir dos arquivos XML, espera-se que dados complexos sejam carregados manualmente pelos desenvolvedores. Além disso, o ''framework'' também espera que os desenvolvedores implementem o método '''executar()''' das classes de execução. As classes pertencentes ao núcleo genérico são apresentadas nas seções seguintes. O diagrama de classes completo do núcleo genérico está disponível no ambiente de desenvolvimento do projeto, em um arquivo do Astah. | + | O núcleo genérico é composto basicamente por classes responsáveis por carregar dados de arquivos XML e executar cenários de teste. A figura abaixo apresenta a lógica de execução proposta na implementação do núcleo genérico. |
+ | |||
+ | [[Arquivo:Execução do framework.png|600px]] | ||
+ | |||
+ | Apesar de alguns dados serem carregados automaticamente pelo ''framework'' a partir dos arquivos XML, espera-se que dados complexos sejam carregados manualmente pelos desenvolvedores por meio do método '''carregarDados()''' da classe de dados implementada. Além disso, o ''framework'' também espera que os desenvolvedores implementem o método '''executar()''' das classes de execução. As classes pertencentes ao núcleo genérico são apresentadas nas seções seguintes. O diagrama de classes completo do núcleo genérico está disponível no ambiente de desenvolvimento do projeto, em um arquivo do Astah. | ||
É importante observar que o ''framework'' é composto basicamente por abstrações de comportamentos esperados de classes que executam e armazenam dados para execução de cenários. É importante manter esta arquitetura de modo a evitar o acoplamento entre as classes do projeto, facilitando o reuso e a substituição destas classes caso necessário. | É importante observar que o ''framework'' é composto basicamente por abstrações de comportamentos esperados de classes que executam e armazenam dados para execução de cenários. É importante manter esta arquitetura de modo a evitar o acoplamento entre as classes do projeto, facilitando o reuso e a substituição destas classes caso necessário. | ||
+ | |||
+ | O código-fonte do núcleo genérico está acessível no GitLab do CNJ, através do endereço http://git.cnj.jus.br/testes/TestCore. A versão compilada do núcleo genérico é mantida no Artifactory do CNJ, no endereço libs-releases-local:br/jus/cnj/tf/test-core. | ||
=== Classe TesteBase === | === Classe TesteBase === | ||
Linha 158: | Linha 164: | ||
=== Interface IResultadoCenario === | === Interface IResultadoCenario === | ||
+ | Analisando isoladamente a estrutura de resultados de cenários pode-se dizer que a proposta é semelhante à arquitetura [http://pt.wikipedia.org/wiki/Pipes_e_filtros dutos e filtros], no sentido de que a execução de cenários pode produzir resultados que são utilizados pelos cenários seguintes. O fluxo de execução dos cenários é ilustrado pelo diagrama de objetos adaptado na figura seguinte. É possível observar, pela figura, que os resultados da execução são passados de um cenário para outro, como um bastão em uma corrida de revezamento. Um objeto de resultado sempre será passado de um cenário para outro, mesmo que esteja vazio (sem resultados produzidos). | ||
− | + | [[Arquivo:Objetos resultados cenário.png|600px]] | |
+ | |||
+ | Tomemos como exemplo um bloco lógico que contenha os cenários de cadastro de processo e de consulta de processo. O objetivo é cadastrar um processo, distribuí-lo e verificar se ele é apresentado corretamente na consulta processual. Neste contexto não é possível informar no arquivo XML do cenário de consulta processual o número do processo, pois ele ainda não existe, visto que o processo ainda não foi distribuído. Para resolver este problema é necessário que o cenário de cadastro de processo produza um resultado que será utilizado posteriormente pela consulta de processo. Isso é possível por meio de classes que implementam a interface '''IResultadoCenario'''. | ||
Esta interface define um comportamento que deve ser apresentado por classes que armazenam resultados de execução de cenários. A figura a seguir apresenta esta interface. | Esta interface define um comportamento que deve ser apresentado por classes que armazenam resultados de execução de cenários. A figura a seguir apresenta esta interface. | ||
Linha 198: | Linha 207: | ||
O núcleo genérico do ''framework'' não fornece qualquer classe que implemente a interface '''ICenario'''. Recomenda-se, desta forma, tal qual apresentado no núcleo Selenium, que seja criada uma camada sobre o núcleo genérico que forneça uma classe (possivelmente abstrata) que implemente o comportamento padrão exigido pela interface, preferencialmente fazendo uso do padrão '''Template Method''' para estruturação de um algoritmo genérico. | O núcleo genérico do ''framework'' não fornece qualquer classe que implemente a interface '''ICenario'''. Recomenda-se, desta forma, tal qual apresentado no núcleo Selenium, que seja criada uma camada sobre o núcleo genérico que forneça uma classe (possivelmente abstrata) que implemente o comportamento padrão exigido pela interface, preferencialmente fazendo uso do padrão '''Template Method''' para estruturação de um algoritmo genérico. | ||
+ | |||
+ | === Integração com Testlink === | ||
+ | O núcleo genérico do ''framework'' permite que resultado da execução dos cenários seja cadastrado diretamente na ferramenta '''Testlink'''. As classes que participam desta atividade são ilustradas na figura seguinte. | ||
+ | |||
+ | [[Arquivo:Integraçao testlink.png|600px]] | ||
+ | |||
+ | A gravação dos resultados é iniciada pelo bloco lógico, visto que um caso de teste no '''Testlink''' é composto por um conjunto de cenários, da mesma forma que o conceito de bloco lógico no ''framework''. O comportamento desta integração é definido pelas classes abstratas '''IntegracaoTestLinkReporterBase''' e '''IntegracaoTestLinkAPIBase'''. Estas classes fornecem assinaturas de métodos que permitem gravar resultdados de execução e anexar arquivos de evidências no Testlink. As classes concretas que realizam a interface de integração foram desenvolvidas com base na API disponível [http://eliasnogueira.com/integracao-selenium-e-testlink neste link]. | ||
== '''ESTRUTURA DO NÚCLEO SELENIUM''' == | == '''ESTRUTURA DO NÚCLEO SELENIUM''' == | ||
O núcleo Selenium é uma camada desenvolvida sobre o núcleo genérico. Pode ser considerado outro ''framework'' construído sobre o ''framework'' genérico. Ele é composto basicamente por classes responsáveis por executar cenários de testes funcionais em páginas HTML. É importante observar que este núcleo Selenium é independente do sistema que é testado. A arquitetura foi concebida para que qualquer sistema acessível pelo navegador e baseado em páginas HTML possa ser testado com base neste núcleo. As classes pertencentes ao núcleo Selenium são apresentadas nas seções seguintes. O diagrama de classes completo deste núcleo está disponível no ambiente de desenvolvimento do projeto, em um arquivo do Astah. | O núcleo Selenium é uma camada desenvolvida sobre o núcleo genérico. Pode ser considerado outro ''framework'' construído sobre o ''framework'' genérico. Ele é composto basicamente por classes responsáveis por executar cenários de testes funcionais em páginas HTML. É importante observar que este núcleo Selenium é independente do sistema que é testado. A arquitetura foi concebida para que qualquer sistema acessível pelo navegador e baseado em páginas HTML possa ser testado com base neste núcleo. As classes pertencentes ao núcleo Selenium são apresentadas nas seções seguintes. O diagrama de classes completo deste núcleo está disponível no ambiente de desenvolvimento do projeto, em um arquivo do Astah. | ||
+ | |||
+ | O código-fonte do núcleo Selenium está acessível no GitLab do CNJ, através do endereço http://git.cnj.jus.br/testes/TestSelenium. A versão compilada do núcleo Selenium é mantida no Artifactory do CNJ, no endereço libs-releases-local:br/jus/cnj/tf/selenium-core. | ||
=== Classe BaseExecucaoSelenium === | === Classe BaseExecucaoSelenium === | ||
Linha 241: | Linha 259: | ||
O exemplo do CRUD acima, bastaria implementar os passos 3 e 4 na classe decorada - metodo executarTeste()) - deixando sem implementação este método na classe docoradora. A forma de utilização de decoração de cenários é apresentada com mais detalhes na seção que documenta a estrutura do arquivo XML de dados de cenários. | O exemplo do CRUD acima, bastaria implementar os passos 3 e 4 na classe decorada - metodo executarTeste()) - deixando sem implementação este método na classe docoradora. A forma de utilização de decoração de cenários é apresentada com mais detalhes na seção que documenta a estrutura do arquivo XML de dados de cenários. | ||
− | Também podem ser aplicados cenários decorados, no caso do PJe, naquelas páginas em que há diferença na execução do cenário em instalações de primeiro e de segundo grau. Na pesquisa de processo, por exemplo, em instalações de segundo grau o sistema permite a informação do órgão julugador colegiado, o que não ocorre em instalações de primeiro grau. Nesta situação, o cenário decorado implementaria a lógica da pesquisa de primeiro grau, e o cenário decorador executaria o cenário decorado, acrescido da lógica para preenchimento do campo de órgão julgador colegiado. | + | Também podem ser aplicados cenários decorados, no caso do PJe, naquelas páginas em que há diferença na execução do cenário em instalações de primeiro e de segundo grau. Na pesquisa de processo, por exemplo, em instalações de segundo grau o sistema permite a informação do órgão julugador colegiado, o que não ocorre em instalações de primeiro grau. Nesta situação, o cenário decorado implementaria a lógica da pesquisa de primeiro grau, e o cenário decorador executaria o cenário decorado, acrescido da lógica para preenchimento do campo de órgão julgador colegiado. As configurações necessárias no arquivo XML para cenários decorados é apresentada [[#configuração de cenários decorados|nesta seção]]. |
=== Classe CenarioSelenium === | === Classe CenarioSelenium === | ||
Linha 264: | Linha 282: | ||
* '''exibirMensagemPosExecucao()''': método abstrato que permite às subclasses implementarem alguma lógica de exibição de mensagens de acordo com o resultado da execução do cenário de teste. | * '''exibirMensagemPosExecucao()''': método abstrato que permite às subclasses implementarem alguma lógica de exibição de mensagens de acordo com o resultado da execução do cenário de teste. | ||
+ | |||
+ | * '''carregarObjetoResultadosCenario()''': método concreto responsável por carregar o objeto com os resultados do cenário. | ||
* '''posTeste()''': método concreto que executa ações que devem ser realizadas após a execução do teste propriamente dita. São ações como logoff na aplicação e fechamento do navegador. | * '''posTeste()''': método concreto que executa ações que devem ser realizadas após a execução do teste propriamente dita. São ações como logoff na aplicação e fechamento do navegador. | ||
+ | |||
+ | Devem ser observadas na implementação do método as chamadas do método '''executarAcoes()''' em cada uma das fases da execução. Estas chamadas são responsáveis por executar as ações definidas para execução automática em cada uma das fases de execução do cenário, conforme descrito [http://www.cnj.jus.br/wikipje/index.php/Testes_automatizados_funcionais_-_passo_a_passo#Fases_de_execu.C3.A7.C3.A3o_do_cen.C3.A1rio nesta seção]. | ||
=== Classe CenarioFragmentoSelenium === | === Classe CenarioFragmentoSelenium === | ||
Linha 350: | Linha 372: | ||
Arquivos XML indexadores são utilizados pelo '''framework''' para indicar quais cenários de quais casos de testes são carregados e executados. A figura seguinte apresenta a estrutura de um arquivo indexador à esquerda, e os dois arquivos de casos de teste à direita. As cores indicam o relacionamento entre o arquivo indexador e os arquivos de casos de teste. | Arquivos XML indexadores são utilizados pelo '''framework''' para indicar quais cenários de quais casos de testes são carregados e executados. A figura seguinte apresenta a estrutura de um arquivo indexador à esquerda, e os dois arquivos de casos de teste à direita. As cores indicam o relacionamento entre o arquivo indexador e os arquivos de casos de teste. | ||
− | [[Arquivo:Indexador.png|1200px]] | + | [[Arquivo:1200px-Indexador.png|1200px]] |
− | A seguir são apresentadas as funções de cada um dos atributos do elemento raiz TestCase: | + | A seguir são apresentadas as funções de cada um dos atributos do elemento raiz '''TestCase''': |
* '''ativo''' (boolean): indica se o caso de teste deve ser executado. Caso o valor seja ''false'' o objeto de indexação de cenários não terá seu método '''executar()''' invocado pelo ''framework'', portanto, os cenários descritos no arquivo não serão executados. Na ausência do atributo o ''framework'' considera o valor como ''true''. | * '''ativo''' (boolean): indica se o caso de teste deve ser executado. Caso o valor seja ''false'' o objeto de indexação de cenários não terá seu método '''executar()''' invocado pelo ''framework'', portanto, os cenários descritos no arquivo não serão executados. Na ausência do atributo o ''framework'' considera o valor como ''true''. | ||
* '''recarregarPagina''' (boolean): indica se a página deve ser recarregada (refresh) antes de iniciar a execução dos cenários descritos no arquivo. Na ausência do atributo o ''framework'' considera o valor como ''false''. | * '''recarregarPagina''' (boolean): indica se a página deve ser recarregada (refresh) antes de iniciar a execução dos cenários descritos no arquivo. Na ausência do atributo o ''framework'' considera o valor como ''false''. | ||
Linha 359: | Linha 381: | ||
* '''maximizarNavegador''' (boolean): indica se o navegador deve ser maximizado antes de iniciar a execução dos cenários descritos no arquivo. Na ausência do atributo o ''framework'' considera o valor como ''false''. | * '''maximizarNavegador''' (boolean): indica se o navegador deve ser maximizado antes de iniciar a execução dos cenários descritos no arquivo. Na ausência do atributo o ''framework'' considera o valor como ''false''. | ||
* '''efetuarLogoff''' (boolean): indica se deve ser feito ''logoff'' no sistema após a execução dos cenários descritos no arquivo. Na ausência do atributo o ''framework'' considera o valor como ''false''. | * '''efetuarLogoff''' (boolean): indica se deve ser feito ''logoff'' no sistema após a execução dos cenários descritos no arquivo. Na ausência do atributo o ''framework'' considera o valor como ''false''. | ||
− | * '''url''' (String): indica a url que deve ser acessada para executar o caso de teste. Se não for informado um valor é usada a definição do arquivo | + | * '''url''' (String): indica a url que deve ser acessada para executar o caso de teste. Se não for informado um valor é usada a definição do arquivo '''test.properties'''. |
* '''nomePlanoTeste''' (String): indica o nome do plano de teste do Test Link usado na intergração. Se não for informado o atributo o framework não enviará para o Test Link o resultado da execução dos blocos lógicos. | * '''nomePlanoTeste''' (String): indica o nome do plano de teste do Test Link usado na intergração. Se não for informado o atributo o framework não enviará para o Test Link o resultado da execução dos blocos lógicos. | ||
− | Conforme descrito, o arquivo indexador é usado para listar os cenários que serão executados. Estes cenários são organizados em [[#Classe BlocoLogico|blocos lógicos]], conforme ilustrado à esquerda entre as linhas 14 e 20 da imagem. Um arquivo indexador pode conter vários blocos lógicos dependendo da necessidade de teste. Dentro de um bloco lógico pode haver tantos cenários quanto necessário para completar um cenário de teste desejado. O nó blocoLogico possui | + | Conforme descrito, o arquivo indexador é usado para listar os cenários que serão executados. Estes cenários são organizados em [[#Classe BlocoLogico|blocos lógicos]], conforme ilustrado à esquerda entre as linhas 14 e 20 da imagem. Um arquivo indexador pode conter vários blocos lógicos dependendo da necessidade de teste. Dentro de um bloco lógico pode haver tantos cenários quanto necessário para completar um cenário de teste desejado. O nó blocoLogico possui os seguintes atributos a conhecer: |
* '''ativo''' (boolean): indica se o bloco lógico deve ser executado. Em arquivos indexadores com muitos blocos lógicos eventualmente é necessário desativar algum. Este atributo pode ser usado para esta finalidade. Na ausência do atributo o ''framework'' considera o valor como ''true''. | * '''ativo''' (boolean): indica se o bloco lógico deve ser executado. Em arquivos indexadores com muitos blocos lógicos eventualmente é necessário desativar algum. Este atributo pode ser usado para esta finalidade. Na ausência do atributo o ''framework'' considera o valor como ''true''. | ||
* '''nome''' (String): nome do bloco lógico usado na geração de log. É importante que o nome guarde relacionamento com a finalidade do bloco lógico. Se o atributo não for informado o ''framework'' irá gerar um nome genérico sequencial para o bloco lógico. | * '''nome''' (String): nome do bloco lógico usado na geração de log. É importante que o nome guarde relacionamento com a finalidade do bloco lógico. Se o atributo não for informado o ''framework'' irá gerar um nome genérico sequencial para o bloco lógico. | ||
* '''testlink''' (String): número do caso de teste no testlink usado para integração. Após a execução do bloco lógico o ''framework'' gera no Test Link um ''log'' com o resultado da execução. Na ausência do atributo o ''framework'' deixará de enviar para o Test Link o resultado da execução do bloco lógico. | * '''testlink''' (String): número do caso de teste no testlink usado para integração. Após a execução do bloco lógico o ''framework'' gera no Test Link um ''log'' com o resultado da execução. Na ausência do atributo o ''framework'' deixará de enviar para o Test Link o resultado da execução do bloco lógico. | ||
+ | * '''gerarEvidencia''' (boolean): indica se devem ser geradas evidências de execução para os cenários contidos no bloco lógico. Na ausência do atributo o ''framework'' considera o valor como ''true''. | ||
Blocos lógicos são formados por cenários de execução. Um bloco lógico pode conter um ou mais cenários, dependendo da necessidade de teste. à esquerda da imagem, nas linhas 16 e 18 são apresentadas duas definições de cenários de execução, cujos atributos são: | Blocos lógicos são formados por cenários de execução. Um bloco lógico pode conter um ou mais cenários, dependendo da necessidade de teste. à esquerda da imagem, nas linhas 16 e 18 são apresentadas duas definições de cenários de execução, cujos atributos são: | ||
Linha 372: | Linha 395: | ||
* '''nome''' (String): nome do cenário que deve ser executado. O ''framework'' utiliza este nome para encontrar a classe cujo objeto será instanciado para execução do cenário. Este nome também é usado para encontrar a classe de dados que armazena os dados para execução. No caso do cenário definido na linha 16, por exemplo, o ''framework'' instanciará um objeto da classe '''Login''' para execução do cenário e um objeto da classe '''DadosLogin''' para armazenamento dos dados de execução. Os dados para execução deste cenário são encontrados no arquivo '''Login.xml'''. A ausência do atributo gera uma exceção e o cenário de teste não é executado. | * '''nome''' (String): nome do cenário que deve ser executado. O ''framework'' utiliza este nome para encontrar a classe cujo objeto será instanciado para execução do cenário. Este nome também é usado para encontrar a classe de dados que armazena os dados para execução. No caso do cenário definido na linha 16, por exemplo, o ''framework'' instanciará um objeto da classe '''Login''' para execução do cenário e um objeto da classe '''DadosLogin''' para armazenamento dos dados de execução. Os dados para execução deste cenário são encontrados no arquivo '''Login.xml'''. A ausência do atributo gera uma exceção e o cenário de teste não é executado. | ||
* '''ref''' (int): indica qual cenário do arquivo de caso de teste será executado. No exemplo da linha 16 o ''framework'' procurará no arquivo '''Login.xml''' pela definição de um cenário cujo atributo '''id''' tenha o mesmo valor que atributo '''ref''', conforme destacado pelas anotações em vermelho à direita da imagem. Na ausência do atributo é lançada uma exceção e o cenário não é executado. | * '''ref''' (int): indica qual cenário do arquivo de caso de teste será executado. No exemplo da linha 16 o ''framework'' procurará no arquivo '''Login.xml''' pela definição de um cenário cujo atributo '''id''' tenha o mesmo valor que atributo '''ref''', conforme destacado pelas anotações em vermelho à direita da imagem. Na ausência do atributo é lançada uma exceção e o cenário não é executado. | ||
+ | * '''gerarEvidencia''' (boolean): indica se o cenário deve geradar evidências de sua execução. Na ausência do atributo o ''framework'' considera o valor como ''true''. | ||
=== Arquivos XML de cenários === | === Arquivos XML de cenários === | ||
Linha 387: | Linha 411: | ||
* '''fecharNavegador''' (boolean): infica se o navegador deve ser fechado após a execução do cenário. Na ausência do atributo o framework considera o valor como ''false'' | * '''fecharNavegador''' (boolean): infica se o navegador deve ser fechado após a execução do cenário. Na ausência do atributo o framework considera o valor como ''false'' | ||
* '''efetuarLogoff''' (boolean): indica se deve ser feito ''logoff'' no sistema após a execução do cenário. Na ausência do atributo o framework considera o valor como ''false'' | * '''efetuarLogoff''' (boolean): indica se deve ser feito ''logoff'' no sistema após a execução do cenário. Na ausência do atributo o framework considera o valor como ''false'' | ||
− | * '''url''' (String): indica a url que deve ser acessada para executar o cenário. Se não for informado um valor é usada a definição do arquivo indexador. Se este último não estiver definido é usado o padrão definido no arquivo ''' | + | * '''url''' (String): indica a url que deve ser acessada para executar o cenário. Se não for informado um valor é usada a definição do arquivo indexador. Se este último não estiver definido é usado o padrão definido no arquivo '''test.properties'''. |
As declarações dentro do nó '''cenario''' são livres, contudo, não pode haver elementos vazios. Três tipos de declarações são esperadas dentro do nó cenário: | As declarações dentro do nó '''cenario''' são livres, contudo, não pode haver elementos vazios. Três tipos de declarações são esperadas dentro do nó cenário: | ||
Linha 400: | Linha 424: | ||
Resultado de cenário é um conjunto de valores produzidos pela execução do cenário. Estes cenários podem ser de CRUD ou qualquer outra operação de sistema. O distribuição de processo é um exemplo de cenário que gera como resultado o número do processo. Este recurso foi criado para que cenários possam compartilhar valores com outros cenários executados posteriormente. A imagem abaixo mostra um trecho do arquivo '''CadastroProcesso.xml''' com a definição de resultados de cenário. | Resultado de cenário é um conjunto de valores produzidos pela execução do cenário. Estes cenários podem ser de CRUD ou qualquer outra operação de sistema. O distribuição de processo é um exemplo de cenário que gera como resultado o número do processo. Este recurso foi criado para que cenários possam compartilhar valores com outros cenários executados posteriormente. A imagem abaixo mostra um trecho do arquivo '''CadastroProcesso.xml''' com a definição de resultados de cenário. | ||
− | [[Arquivo: | + | [[Arquivo:Cadastroprocessoxml.png]] |
Entre as linhas 39 e 42 é apresentada a definição de resultados de cenário. Dentro da ''tag'' '''resultados''' (no plural) devem ser declaradas uma ou mais ''tags'' '''resultado''' (no singular). Dentro das ''tags'' '''resultado''' devem ser informados nomes de atributos declarados na classe de dados de cadastro de processo. Ou seja, a classe '''DadosCadastroProcesos''' deve possuir os atributos '''numeroProcesso''' e '''cpfParteIntimacao''', assim como seus métodos '''get''' e '''set'''. Com esta definição, após a execução do cenário o ''framework'' criará uma lista com o número do processo e com o CPF da parte que será intimada. | Entre as linhas 39 e 42 é apresentada a definição de resultados de cenário. Dentro da ''tag'' '''resultados''' (no plural) devem ser declaradas uma ou mais ''tags'' '''resultado''' (no singular). Dentro das ''tags'' '''resultado''' devem ser informados nomes de atributos declarados na classe de dados de cadastro de processo. Ou seja, a classe '''DadosCadastroProcesos''' deve possuir os atributos '''numeroProcesso''' e '''cpfParteIntimacao''', assim como seus métodos '''get''' e '''set'''. Com esta definição, após a execução do cenário o ''framework'' criará uma lista com o número do processo e com o CPF da parte que será intimada. | ||
Linha 435: | Linha 459: | ||
Neste trecho de código do arquivo '''CadastroEnteAutoridade.xml''' é possível observar que o segundo cenário ('''id=2''') define apenas um atributo ('''nomeEnteAutoridade'''), sendo que todos os demais são copiados do primeiro cenário ('''id=1''') por meio da definição '''ref=1'''. | Neste trecho de código do arquivo '''CadastroEnteAutoridade.xml''' é possível observar que o segundo cenário ('''id=2''') define apenas um atributo ('''nomeEnteAutoridade'''), sendo que todos os demais são copiados do primeiro cenário ('''id=1''') por meio da definição '''ref=1'''. | ||
− | ==== | + | ==== Configuração de cenários decorados ==== |
O conceito de cenários decorados está apresentado na [[#Cenários decorados|seção específica]]. Entretanto, resta apresentar a estrutura de um arquivo XML que utiliza este conceito. Nunca é demais ressaltar que o objetivo do uso de cenários decorados é o reaproveitamento de código e de dados sempre que viável. | O conceito de cenários decorados está apresentado na [[#Cenários decorados|seção específica]]. Entretanto, resta apresentar a estrutura de um arquivo XML que utiliza este conceito. Nunca é demais ressaltar que o objetivo do uso de cenários decorados é o reaproveitamento de código e de dados sempre que viável. | ||
− | Como exemplificado nas seções anteriores, cenários de cadastro e alterações de registros no PJe são bastante semelhantes em sua forma de execução. A ideia básica é acessar um item de menu para acessar a página, preencher dados, salvar o registro e conferir a mensagem exibida pelo sistema. | + | Como exemplificado nas seções anteriores, cenários de cadastro e alterações de registros no PJe são bastante semelhantes em sua forma de execução. A ideia básica é acessar um item de menu para acessar a página, preencher dados, salvar o registro e conferir a mensagem exibida pelo sistema. Em situações como esta é possível o reaproveitamento de código por herança ou por composição. O ''framework'' utiliza este último conceito para a implementação de cenários decorados, com base no padrão '''Decorator'''. |
+ | |||
+ | Utilizar ou não cenários decorados é uma decisão do desenvolvedor de cenários de teste, visto que o recurso sempre está disponível no ''framework''. Esta decisão é expressada por meio de declarações no arquivo XML de dados. O trecho de código do arquivo de dados '''CadastroProcuradoria2G.xml''' apresentado abaixo apresenta a declaração de uso de cenários decorados. | ||
+ | |||
+ | [[Arquivo:Cadastroprocuradoria2g.png|700px]] | ||
+ | |||
+ | O recurso de cenários decorados possui duas declarações de atributos do nó '''cenario'''. A declaração '''decorator=CadastroProcuradoria1G''' indica que o framework deve executar o algoritmo de execução do cenário '''CadastroProcuradoria1G'''. Já a declaração '''refDecorator=1''' indica que o algoritmo deve ser executado com os dados presente no cenário com '''id=1''' da classe de execução '''CadastroProcuradoria1G'''. Se não for definido un valor para o atributo '''refDecorator''', ou se este atributo estiver ausente, o ''framework'' ainda executará o o algoritmo da classe '''CadastroProcuradoria1G''', contudo, usará os dados do próprio cenário da imagem acima. | ||
+ | |||
+ | O trecho de código abaixo apresenta o arquivo XML do cenário '''CadastroProcuradoria1G''' que é referenciado pelo cenário '''CadastroProcuradoria2G'''. | ||
+ | |||
+ | [[Arquivo:CadastroProcuradoria1G.png|700px|frame|none|Figura 1]] | ||
+ | |||
+ | == '''ESTRUTURA PARA EXECUÇÃO AUTOMÁTICA DE AÇÕES''' == | ||
+ | [http://www.cnj.jus.br/wikipje/index.php/Testes_automatizados_funcionais_-_passo_a_passo#A.C3.87.C3.95ES_AUTOM.C3.81TICAS Esta seção] descreve do ponto de vista do usuário a utilização de ações automatizadas. Do ponto de vista do desenvolvedor mantenedor do framework alguns aspectos técnicos devem ser considerados. O núcleo genérico provê um conjunto de classes que permite a execução autoḿatica de ações. Entretanto, o núcleo genérico em si não fornece qualquer ação para ser executada. A figura seguinte apresenta um diagrama completo deste projeto. | ||
+ | |||
+ | [[Arquivo:Acoes.png|800px]] | ||
+ | |||
+ | Como pode ser observado pelo diagrama, a estrutura de gerenciamento de ações é fornecida pela classe de dados, visto que as ações são configuradas no próprio arquivo xml, que é carregado pela classe de dados. Contudo, o núcleo genérico, além de não fornecer as próŕias ações, também não fornece qualque implementação para carregamento das ações: as subclasses devem implementar estas lógicas. | ||
+ | |||
+ | Na implementação atual do ''framework'', as lógicas de carga de ações (leitura do arquivo xml) e o comportamento das ações são disponibilizados pelo núcleo Selenium (classes de cor laranja no diagrama acima). A carga das ações obedece uma lógica adequada para reconhecer a estrutura de declarações contida [http://www.cnj.jus.br/wikipje/index.php/Testes_automatizados_funcionais_-_passo_a_passo#A.C3.87.C3.95ES_AUTOM.C3.81TICAS nesta seção]. Caso outra lógica seja necessária, deve ser criada outra classe no núcleo Selenium que estenda a superclasse '''CarregadorAcoesBase'''. Esta classe deve ser informada no arquivo de [http://www.cnj.jus.br/wikipje/index.php/Testes_automatizados_funcionais_-_passo_a_passo#Configura.C3.A7.C3.B5es_do_arquivo_test.properties '''test.properties'''] para o ''framework'' saber qual classe deve instanciar para realizar a carga das ações. | ||
== '''REFERÊNCIAS''' == | == '''REFERÊNCIAS''' == | ||
+ | |||
+ | Artigo sobre testes funcionais automatizados apresentado no PLoP 2014: https://docs.google.com/document/d/1agC_bLN2wEEIj3uLqzEIcwQibhX7dA49gxKS0Bj6E7Y/edit | ||
Modelador UML utilizado no projeto: http://astah.net/editions/community | Modelador UML utilizado no projeto: http://astah.net/editions/community | ||
Linha 458: | Linha 503: | ||
Design Patterm Singleton: http://www.dofactory.com/net/singleton-design-pattern | Design Patterm Singleton: http://www.dofactory.com/net/singleton-design-pattern | ||
+ | |||
+ | Dutos e filtros: http://pt.wikipedia.org/wiki/Pipes_e_filtros | ||
Composição ''versus'' herança para reuso de código: http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html | Composição ''versus'' herança para reuso de código: http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html | ||
+ | |||
+ | Integração do Selenium com Testlink: http://eliasnogueira.com/integracao-selenium-e-testlink | ||
+ | |||
+ | [[Category:PJe2]] |
Edição atual tal como às 14h31min de 21 de janeiro de 2016
Conteúdo |
[editar] INTRODUÇÃO
O teste funcional automatizado consiste basicamente em executar um mesmo algoritmo com diferentes massas de dados com objetivo de atestar o funcionamento de funções de acordo com a especificação de requisitos. Para cenários diferentes são usados dados diferentes com resultados esperados diferentes, e inserir estes dados e resultados diretamente no código do algoritmo implica em replicar (tantas vezes quantos cenários diferentes existirem) código para poder atender a diferentes situações.
O objetivo de se desenvolver um framework para automação dos testes é permitir que seja criada uma camada de dados independente da camada de algoritmos que executam a automação. Esta é a tradicional divisão de software em duas camadas. Esta divisão (algoritmo versus dados) é válida tanto para testes automatizados funcionais como para testes unitários.
Este conteúdo descreve a documentação técnica para manutenção do framework de testes automatizados a ser utilizado para testes unitários e testes funcionais. O documento está organizado em seções que descrevem separadamente as características de cada parte da arquitetura proposta. Para facilitar a identificação das classes dos núcleos, elas estão coloridas de acordo com o núcleo da qual fazem parte, conforme ilustrado pela legenda a seguir.
Caso o interesse do leitor seja na documentação sobre como utilizar o framework para automação de testes, ela pode ser obtida neste link.
[editar] VISÃO ARQUITETURAL POR SERVIÇO PRESTADO
A arquitetura do framework desenvolvido pode ser vista sob o ponto de vista da funcionalidade fornecida para os desenvolvedores, conforme ilustrado na figura abaixo.
[editar] Núcleo genérico
O núcleo genérico do framework tem a função de carregar os dados dos cenários (provenientes de arquivos XML), carregar os cenários e executá-los, independentemente se esta execução for um teste funcional com Selenium ou de um teste unitário. Trata-se de um conjunto de classes, em sua maioria abstratas, que definem comportamentos esperados da funcionalidade de testes automatizados.
[editar] Núcleo Selenium
O núcleo Selenium é uma camada sobre o núcleo genérico, que usa os serviços deste (por herança e composição) para a execução de testes funcionais em páginas web. É um conjunto de classes, em sua maioria também abstratas que, além de estender as classes do núcleo genérico, faz uso do Selenium WebDriver para acessar o navegador, preencher os campos de formulário e conferir os valores dos elementos HTML.
É importante observar que esta camada é independente do sistema web alvo do teste. Ou seja, qualquer sistema web com interface HTML/XHTML pode ter seus testes funcionais automatizados com base no framework desenvolvido.
[editar] Núcleo PJe
Nesta camada reside uma infraestrutura pra facilitar a automação dos casos de teste do Pje. Elas são desenvolvidas segundo o padrão Page Object e, na hierarquia do framework, estendem funcionalidades de FluentPage. Trata-se de um conjunto de classes abstratas que implementam alguns dos métodos exigidos pelas demais camadas do framework.
[editar] VISÃO ARQUITETURAL POR CAMADAS
Sob outro ponto de vista o framework pode ser analisado de acordo com a divisão tradicional da arquitetura em camadas, com divisão de dados e lógica de negócio. A Figura a seguir exibe a arquitetura dividida em camadas.
[editar] Arquivo xml cenários
Este arquivo é responsável por armazenar os dados para execução dos cenários. Para cada caso de teste é necessário criar um arquivo xml com os dados para sua execução. Em um arquivo podem ser definidos vários cenários para execução, com valores diferentes para cada um deles.
[editar] Arquivo xml indexador
Este arquivo funciona como um conjunto de links para os cenários que serão executados, cenários estes cujos dados de execução são definidos no arquivo xml descrito na seção anterior.
De acordo com a relação entre o arquivo indexador e o arquivo de cenários ilustrada na Figura acima, um arquivo indexador fornece links para vários arquivos de cenários.
Em geral deve haver poucos arquivos de indexação de cenários, pois cada um deles referencia inúmeros arquivos de cenários.
[editar] Classes de indexação de cenários
Classes de indexação são as classes chamadas pelo Junit como ponto de partida para a automação. No framework existe apenas uma classe, abstrata, chamada TesteBase, e que utiliza apenas a anotação @Test do Junit.
Para cada arquivo xml indexador descrito na seção anterior deve existir uma classe de indexação criada pelo desenvolvedor. Esta classe deve estender TesteBase, somente; nenhuma outra implementação é necessária.
O nome da classe também deve guardar relação com o nome do arquivo XML indexador. Por exemplo, se o nome do arquivo indexador for Cenarios1G.xml, o nome da classe deverá ser Cenarios1GTest. Ou seja, o nome da classe deve possuir o sufixo Test. No padrão de desenvolvimento do framework esta classe deve ser criada abaixo da estrutura src/test/java, conforme ilustrado pela figura abaixo.
[editar] Relação entre os nomes das classes e arquivos XML
- Classe indexadora de cenários: o nome da classe indexadora é livre, contudo, deve terminar com o sufixo Test. Por exemplo, para criar uma classe indexadora de cenários de teste para processos de primeiro grau o nome da classe poderia ser Cenarios1GTest.
- Arquivo XML indexador: o nome do arquivo XML indexador deve ser idêntico ao nome da classe indexadora de cenários, porém, sem o sufixo Test. No caso do exemplo acima, o nome do arquivo XML indexador de cenários de teste para processos de primeiro grau seria Cenarios1G.xml.
- Classes de execução de cenários: o nome da classe de execução é livre. Por exemplo, para um caso de teste de cadastro de processo o nome da classe poderia ser CadastroProcesso.
- Classes de dados de cenários: o nome da classe de dados de cenários deve ser idêntico ao nome da classe de execução de cenários, porém, acrescido do prefixo Dados. No exemplo acima, o nome da classe de dados de cenários para um caso de teste de cadastro de processo seria DadosCadastroProcesso.
- Arquivo XML de cenários: o nome do arquivo XML de cenários deve ser idêntico ao nome da classe de execução de cenários. No exemplo acima, o nome do arquivo XML de cenários para um caso de teste de cadastro de processo seria CadastroProcesso.xml.
[editar] Relacionamento entre as classes das camadas
O framework desenvolvido é composto por um conjunto de classes abstratas com métodos abstratos cuja implementação deve ser realizadas pelas classes concretas de execução e de dados. As classes abstratas definem também métodos template concretos que, de acordo com a definição do padrão Template Method, têm em sua implementação a chamada de métodos hook abstratos que devem ser implementados pelas subclasses.
A figura abaixo apresenta um diagrama de sequência que ilustra a dinâmica de comunicação entre as classes do framework. Trata-se apenas de uma visão geral, e não da real implementação desta lógica.
A sequência descrita no diagrama é executada para cada uma das classes que herdam da superclasse TesteBase. A seguir é apresentada uma breve descrição dos passos ilustrados no diagrama:
* chamada 1: para cada subclasse de TesteBase o JUnit instancia um objeto daquela classe. Este objeto possui uma lista de cenários a serem executados que é descrita no arquivo XML indexador que tem o mesmo nome da classe, sem o sufixo Test.
* chamada 1.1: para cada objeto criado são instanciados (dinamicamente, por reflexão) tantos objetos quantos descritos no arquivo indexador. Estes objetos são instâncias de classes que implementam a interface ICenario. São objetos que irão executar a lógica do teste.
* cahamada 1.1.1: cada objeto criado na chamada 1.1 cria (dinamicamente, por reflexão) seu objeto de dados. Este objeto é uma instância de uma subclasse de DadosBase. A subclasse de dados tem o mesmo nome da subclasse de execução acrescido do prefixo Dados.
* chamada 1.1.2: cada objeto criado na chamada 1.1 invoca o método carregarDados() de seu objeto de dados. Este método é abstrato e deve ser implementado pelas subclasses, com objetivo de carregar os dados do arquivo XML do cenário.
* chamada 2: para cada objeto criado na chamada 1 o JUnit invoca o método executar(). Este método é o único da superclasse TesteBase que possui a anotação @Test, necessária para o JUnit identificar os métodos que devem ser invocados.
* chamada 2.1: para cada objeto de ICenario é invocado o método executar(), que efetivamente executa o cenário de teste com os dados carregados na chamada 1.1.2.
[editar] ESTRUTURA DO NÚCLEO GENÉRICO
O núcleo genérico é composto basicamente por classes responsáveis por carregar dados de arquivos XML e executar cenários de teste. A figura abaixo apresenta a lógica de execução proposta na implementação do núcleo genérico.
Apesar de alguns dados serem carregados automaticamente pelo framework a partir dos arquivos XML, espera-se que dados complexos sejam carregados manualmente pelos desenvolvedores por meio do método carregarDados() da classe de dados implementada. Além disso, o framework também espera que os desenvolvedores implementem o método executar() das classes de execução. As classes pertencentes ao núcleo genérico são apresentadas nas seções seguintes. O diagrama de classes completo do núcleo genérico está disponível no ambiente de desenvolvimento do projeto, em um arquivo do Astah.
É importante observar que o framework é composto basicamente por abstrações de comportamentos esperados de classes que executam e armazenam dados para execução de cenários. É importante manter esta arquitetura de modo a evitar o acoplamento entre as classes do projeto, facilitando o reuso e a substituição destas classes caso necessário.
O código-fonte do núcleo genérico está acessível no GitLab do CNJ, através do endereço http://git.cnj.jus.br/testes/TestCore. A versão compilada do núcleo genérico é mantida no Artifactory do CNJ, no endereço libs-releases-local:br/jus/cnj/tf/test-core.
[editar] Classe TesteBase
É uma classe abstrata de cujas subclasses são instanciados objetos para execução do método marcado com a anotação @Test. Esta classe possui um método concreto executar(), que é o único da classe marcado com a anotação @Test.
No contexto do framework, executar() é um método projetado de acordo com o padrão Template Method. O trecho de código a seguir ilustra a implementação resumida deste método.
Os métodos preTeste() e posTeste() são abstratos e devem ser implementados pelas subclasses de TesteBase. No método preTeste() devem ser implementadas ações que se espera realizar antes de ser iniciada a execução dos cenários. Um exemplo de ação a ser implementada no preTeste() seria a execução de determinados scrips para preparação do banco de dados ou a execução de logon no sistema.
Já no método posTeste() devem ser implementadas ações que devem ser executadas após a execução dos cenários. Um exemplo de ação a ser implementada no posTeste() seria a execução de determinados scripts para limpeza do banco de dados ou a execução de logoff no sistema.
Por fim, o método concreto executarTeste() implementa a chamada do objeto (da classe ControleTesteBase) que de fato executa os cenários, com interação pela lista de cenários definidos no arquivo indexador de cenários.
Esta classe ainda possui um atributo da classe ControleTesteBase, responsável por realizar a carga do arquivo XML indexador. A seção seguinte apresenta a documentação desta classe.
[editar] Classe ControleTesteBase
É uma classe abstrata responsável por controlar a iteração entre os cenários que serão executados. Contudo, ela não executa diretamente os cenários. Em vez disso, esta classe mantém uma lista de blocos lógicos que contém cenários que devem ser executados em conjunto para completar um caso de teste. O conceito de bloco lógico é apresentado da seção seguinte. O trecho de código a seguir apresenta as principais características da classe ControleTesteBase.
O método executarCenarios() é responsável por varrer a lista de blocos lógicos e, para cada um deles, invocar o método executar dos respectivos cenários de execução. Além de executar os cenários esta classe também faz a carga de fato dos cenários descritos no arquivo XML indexador.
A classe ControleTesteBase tem ainda um atributo da classe ControleBlocosLogicos, que controla os blocos lógicos de cenários. O conceito de bloco logico é apresentado na seção seguinte.
[editar] Classe BlocoLogico
Um bloco lógico é um conjunto de cenários de teste que devem ser executados atomicamente para que o caso de teste seja considerado bem sucedido. Se a execução de algum cenário do bloco lógico falhar o resultado do teste deste bloco lógico é considerado falho. Os blocos lógicos são definidos no arquivo XML indexador, conforme apresentado na seção específico sobre o arquivo indexador.
O próprio objeto de bloco lógico realiza a carga de seus cenários a partir do arquivo XML indexador, e também executa estes cenários de teste. O trecho de código seguinte apresenta as principais características da classe.
Além destas características a classe BlocoLogico mantém uma lista de cenários de execução, que são objetos de classes que implementam a interface ICenario. A função do método executarCenarios() é exatamente invocar o método executar() de cada um destes objetos.
[editar] Classe DadosBase
É uma classe abstrata que define uma lógica para carga de dados a partir de arquivos XML. Além de manipular os dados para execução esta classe é responsável por armazenar os resultados da execução do cenário bem como os resultados esperados da execução. Estes dois últimos conceitos são devidamente apresentados nas seções seguintes.
O método responsável por realizar esta carga de dados é o método inicializar(). O trecho de código a seguir ilustra a implementação resumida deste método.
O método concreto inicializar() implementa o padrão Template Method, definindo um esqueleto de algoritmo que deve ser implementado pelas subclasses.
O método concreto copiarDados() é responsável por copiar dados de outro cenário para execução. Este conceito é apresentado mais detalhadamente no capítulo de documentação dos arquivos XML. Contudo, em resumo, o framework permite que se defina um cenário (seus dados) e, em vez de se informar os dados na própria definição do cenário no arquivo XML, copia-se estes dados de outro cenário já definido, com objetivo de reutilizar estes dados.
Por sua vez, o método concreto carregarDadosAutomaticos() permite que o próprio framework preencha os atributos da classe de dados de acordo com os valores informados no arquivo XML. Isso é possível sempre que os nós de dados não tenham filhos. O trecho de código a seguir ilustra um exemplo de cenário definido num arquivo XML em que os dados são carregados automaticamente pelo framework.
Para que seja possível a carga automática dos dados a classe de dados que estende DadosBase deve possuir todos os atributos definidos no arquivo XML. Por exemplo, deve haver atributos para email, telefoneDDD, telefoneNumero e cadastrarPush, com seus métodos get e set. Todos os atributos definidos no arquivo XML devem estar implementados na classe de dados, entretanto, o contrário não é verdadeiro. Ou seja, Os atributos definidos na classe de dados não precisam ter seus valores definidos no arquivo XML. Isso porque alguns atributos podem ter seus valores setados com valores provenientes do resultado da execução de cenários anteriores. Este conceito é apresentado apropriadamente em seção específica.
[editar] Classe ControleExecucaoBase
Esta classe é responsável por encapsular o objeto de dados da classe DadosBase. Ela cria o objeto de dados e retorna uma referência para ele, além de controlar se o cenário foi executado com sucesso ou não.
[editar] Interface IResultadoCenario
Analisando isoladamente a estrutura de resultados de cenários pode-se dizer que a proposta é semelhante à arquitetura dutos e filtros, no sentido de que a execução de cenários pode produzir resultados que são utilizados pelos cenários seguintes. O fluxo de execução dos cenários é ilustrado pelo diagrama de objetos adaptado na figura seguinte. É possível observar, pela figura, que os resultados da execução são passados de um cenário para outro, como um bastão em uma corrida de revezamento. Um objeto de resultado sempre será passado de um cenário para outro, mesmo que esteja vazio (sem resultados produzidos).
Tomemos como exemplo um bloco lógico que contenha os cenários de cadastro de processo e de consulta de processo. O objetivo é cadastrar um processo, distribuí-lo e verificar se ele é apresentado corretamente na consulta processual. Neste contexto não é possível informar no arquivo XML do cenário de consulta processual o número do processo, pois ele ainda não existe, visto que o processo ainda não foi distribuído. Para resolver este problema é necessário que o cenário de cadastro de processo produza um resultado que será utilizado posteriormente pela consulta de processo. Isso é possível por meio de classes que implementam a interface IResultadoCenario.
Esta interface define um comportamento que deve ser apresentado por classes que armazenam resultados de execução de cenários. A figura a seguir apresenta esta interface.
O objeto que se comporta desta maneira é semelhante a um mapa de valores, com um conjunto de pares valor e chave. O framework disponibiliza uma classe concreta que implementa esta interface, tal qual o trecho de código a seguir.
Apesar de o framework disponbilizar uma classe concreta simples para tratar os resultados, nada impede o desenvolvedor de criar uma nova classe estendendo ou decorando (padrão Decorator) a classe fornecida.
[editar] Resultados esperados e resultados encontrados
A execução de um cenário de teste envolve um conjunto de valores de entrada e um ou mais resultados esperados. Estes resultados podem ser uma mensagem na tela ou uma lista de objetos apresentados em uma página HTML na forma de tabela. Para tratar este último caso o framework possui o conceito de Resultados Esperados. Não se deve confundir este conceito com o conceito de Resultados (implementação de IResultadoCenario), apresentado na seção anterior.
A consulta de processo, por exemplo, é um cenário que possui resultados esperados. Ao criar um cenário de teste para consulta processual, a partir de um conjunto de parâmetros de pesquisa é possível que sejam determinados quais processos devem retornar nesta consulta. Estes processos retornados devem ser confrontados com a lista de processos esperados, para aferição do sucesso da execução do cenário.
Neste contexto, as classes GerenciadorResultadosBase, ComparadorResultadosBase e ResultadoBase (as duas últimas apresentadas nas seções seguintes) implementam a lógica de carga e comparação de resultados esperados com resultados encontrados. Resultados encontrados são aqueles apresentados após a execução de cenário. Pode ser uma página HTML ou simplesmente uma lista de objetos retornados da execução de um método.
O diagrama ilustrado na figura a seguir mostra o relacionamento entre as classes GerenciadorResultadosBase e ComparadorResultadosBase.
A responsabilidade da classe GerenciadorResultadosBase é basicamente carregar os resultados esperados antes da execução dos cenários e os resultados encontrados após a execução. Importante salientar, contudo, que os resultados esperados e os resultados encontrados são objetos de classes que estendem a classe ResultadoBase.
Os resultados esperados são carregados automaticamente a partir do arquivo XML dos dados do cenário. Já os resultados encontrados devem ser carregados manualmente pelos desenvolvedores. Também é possível a construção de uma camada sobre o núcleo genérico que implemente alguma lógica de carga automática destes valores.
Após a execução dos cenários o framework tem uma lista de resultados esperados e outra lista de resultados encontrados. Com isso a classe GerenciadorResultadosBase pode fazer uso de um objeto da classe ComparadorResultadosBase base comparar os resultados. O cenário somente passará no teste se ambas as listas contiverem os mesmos objetos, independentemente da ordem em que estiverem na lista.
Importante observar que as três classes que fazem parte desta lógica são abstratas, apesar de implementarem o comportamento descrito nesta seção. O núcleo genérico do framework não oferece para esta funcionalidade classes concretas que implementem este comportamento, sendo responsabilidade do desenvolvedor prover estas classes. A camada acima do núcleo genérico, o núcleo Selenium, provê uma classe concreta para gerenciamento dos resultados da execução de cenários de casos de teste funcionais com Selenium.
[editar] Interface ICenario
A interface ICenario descreve o comportamento esperado de classes que executam cenários de teste. Para utilizar o framework o desenvolvedor deve fornecer classes concretas que implementem este comportamento. A figura seguinte apresenta o comportamento esperado das classes de execução fornecidas pelos desenvolvedores.
O núcleo genérico do framework não fornece qualquer classe que implemente a interface ICenario. Recomenda-se, desta forma, tal qual apresentado no núcleo Selenium, que seja criada uma camada sobre o núcleo genérico que forneça uma classe (possivelmente abstrata) que implemente o comportamento padrão exigido pela interface, preferencialmente fazendo uso do padrão Template Method para estruturação de um algoritmo genérico.
[editar] Integração com Testlink
O núcleo genérico do framework permite que resultado da execução dos cenários seja cadastrado diretamente na ferramenta Testlink. As classes que participam desta atividade são ilustradas na figura seguinte.
A gravação dos resultados é iniciada pelo bloco lógico, visto que um caso de teste no Testlink é composto por um conjunto de cenários, da mesma forma que o conceito de bloco lógico no framework. O comportamento desta integração é definido pelas classes abstratas IntegracaoTestLinkReporterBase e IntegracaoTestLinkAPIBase. Estas classes fornecem assinaturas de métodos que permitem gravar resultdados de execução e anexar arquivos de evidências no Testlink. As classes concretas que realizam a interface de integração foram desenvolvidas com base na API disponível neste link.
[editar] ESTRUTURA DO NÚCLEO SELENIUM
O núcleo Selenium é uma camada desenvolvida sobre o núcleo genérico. Pode ser considerado outro framework construído sobre o framework genérico. Ele é composto basicamente por classes responsáveis por executar cenários de testes funcionais em páginas HTML. É importante observar que este núcleo Selenium é independente do sistema que é testado. A arquitetura foi concebida para que qualquer sistema acessível pelo navegador e baseado em páginas HTML possa ser testado com base neste núcleo. As classes pertencentes ao núcleo Selenium são apresentadas nas seções seguintes. O diagrama de classes completo deste núcleo está disponível no ambiente de desenvolvimento do projeto, em um arquivo do Astah.
O código-fonte do núcleo Selenium está acessível no GitLab do CNJ, através do endereço http://git.cnj.jus.br/testes/TestSelenium. A versão compilada do núcleo Selenium é mantida no Artifactory do CNJ, no endereço libs-releases-local:br/jus/cnj/tf/selenium-core.
[editar] Classe BaseExecucaoSelenium
Trata-se de uma classe abstrata que estende funcionalidades da classe FluentPage, do framework FluentLenium. Ela fornece basicamente métodos simplificados para encontrar elementos em páginas HTML.
Esta classe mantém referência para um objeto da classe ControleExecucaoBase, que é a classe responsável por encapsular o objeto que mantém os dados para execução dos cenários. Além disso ela faz a inicialização do driver do navegador que é usado na automação dos testes. Também é responsabilidade desta classe agregar um objeto para execução de cenários decorados.
[editar] Classe ControleTesteSelenium
A classe ControleTesteSelenium estende a superclasse ControleTesteBase. No núcleo Selenium a classe ControleTesteSelenium não implementa qualquer lógica além daquela definida na superclasse. Ela é fornecida no núcle Selenium para que não seja necessário definir a classe concreta em cada um dos cenários que são implementados sobre este núcleo.
[editar] Cenários decorados
Um princípio de construção do framework é o reaproveitamento de código de dados e de lógica de execução. O próprio conceito de framework está fortemente relacionado com reuso de código. Neste contexto os cenários decorados buscam aplicar o reaproveitamento para dados e lógica de execução.
Imaginemos um teste automatizado de uma página de CRUD (Create, Read, Update e Delete). As funções de criar e atualizar um registro de dados podem ser bastante semelhantes dependendo da forma de construção das páginas. Por exemplo, uma função de cadastro de usuário pode ter os seguintes passos:
- acessar o menu de cadastro de usuário.
- pressionar o botão NOVO para iniciar a criação de um registro.
- preencher os campos do formulário com as informações.
- pressionar o botão SALVAR.
- verificar a mensagem "registro criado com sucesso".
Já a função de atualizar um cadastro de usuário pode ter a seguinte lógica:
- acessar o menu de alteração de usuário.
- clicar sobre um usuário para atualizar o registro.
- preencher os campos do formulário com as informações.
- pressionar o botão SALVAR.
- verificar a mensagem "registro alterado com sucesso".
Como pode-se observar, as tarefas de cadastrar um usuário e de atualizar um registro de usuário têm os passos 3 e 4 idênticos. A lógica é a mesma, apesar de os dados serem diferentes. É esta lógica que o recurso de cenários decorados busca reaproveitar. E, eventualmente, se for conveniente, pode-se aproveitar inclusive os dados de execução do cenário.
Este reaproveitamento de comportamento dos cenários é realizado pela aplicação do padrão Decorator. Este padrão faz uso da técnica de composição de objetos para reuso de código. Desta forma, o código comum é encapsulado num objeto que, posteriormente será decorado por outro objeto com comportamentos adicionais. Em resumo, se um objeto (aqui chamado decorado) faz a atividade ABC, outro objeto (chamado decorador) também fará as mesmas atividades, acrescidas das atividades XYZ.
Por padrão todo objeto de execução de cenários criado no núcleo Selenium possui um objeto que pode ser decorado. A decisão de usar ou não este objeto é transferida para o desenvolvedor do cenário de testes, por meio da configuração do cenário no arquivo XML de dados. Por padrão o cenário decorado será executado sempre antes do cenário principal.
O exemplo do CRUD acima, bastaria implementar os passos 3 e 4 na classe decorada - metodo executarTeste()) - deixando sem implementação este método na classe docoradora. A forma de utilização de decoração de cenários é apresentada com mais detalhes na seção que documenta a estrutura do arquivo XML de dados de cenários.
Também podem ser aplicados cenários decorados, no caso do PJe, naquelas páginas em que há diferença na execução do cenário em instalações de primeiro e de segundo grau. Na pesquisa de processo, por exemplo, em instalações de segundo grau o sistema permite a informação do órgão julugador colegiado, o que não ocorre em instalações de primeiro grau. Nesta situação, o cenário decorado implementaria a lógica da pesquisa de primeiro grau, e o cenário decorador executaria o cenário decorado, acrescido da lógica para preenchimento do campo de órgão julgador colegiado. As configurações necessárias no arquivo XML para cenários decorados é apresentada nesta seção.
[editar] Classe CenarioSelenium
Esta classe estende a classe BaseExecucaoSelenium e implementa a interface ICenario, e é responsável pelo fluxo de execução do teste automatizado funcional em páginas HTML. O trecho de código seguinte apresenta o algoritmo de execução do teste implementado pela classe. Trata-se de uma implementação do padrão Template Method, que define um esqueleto de algoritmo que pode ser complementado pelas subclasses.
A seguir são apresentadas descrições dos métodos hook invocados no método executar():
- preTeste(): método concreto que executa ações que devem ser realizadas antes de se iniciar a execução do teste. São ações como abertura do navegador, maximização da janela, acesso ao perfil específico e acesso ao menu específico para execução do cenário.
- preExecucaoTeste(): método abstrato que abre oportunidade para as subclasses implementarem ações a serem executadas antes de os testes serem executados.
- executarCenarioDecorator(): método abstrato que abre oportunidade para as subclasses implementarem comportamento específico para os cenários decorados.
- executarTeste(): método abstrato que permite às subclasses implementarem a lógica de execução do cenário de teste. É o método mais importante desta camada do framework e obrigatoriamente deve ser implementado. É este método que contém os comandos do Selenium ou do FluentLenium para preencher os formulários HTML das páginas e executar as ações dos cenários de teste.
- posExecucaoTeste(): método abstrato abre oportunidade para as subclasses implementarem ações a serem executadas após de os testes serem executados.
- conferirResultados(): método abstrato que permite às subclasses implementarem alguma lógica de conferência dos resultados esperados com os resultados encontrados.
- exibirMensagemPosExecucao(): método abstrato que permite às subclasses implementarem alguma lógica de exibição de mensagens de acordo com o resultado da execução do cenário de teste.
- carregarObjetoResultadosCenario(): método concreto responsável por carregar o objeto com os resultados do cenário.
- posTeste(): método concreto que executa ações que devem ser realizadas após a execução do teste propriamente dita. São ações como logoff na aplicação e fechamento do navegador.
Devem ser observadas na implementação do método as chamadas do método executarAcoes() em cada uma das fases da execução. Estas chamadas são responsáveis por executar as ações definidas para execução automática em cada uma das fases de execução do cenário, conforme descrito nesta seção.
[editar] Classe CenarioFragmentoSelenium
A execução de um cenário pode envolver uma série de passos e o preenchimento de uma série de páginas. Por exemplo, fazer uma compra na internet pode envolver alguns passos como escolher o produto, informar o endereço de entrega, escolher a forma de pagamento e realizar o pagamento. No caso do PJe, um exemplo é o cadastro de processo, que envolve o preenchimento de várias abas no sistema. Em situações em que é necessário o preenchimento de várias páginas o recurso de fragmento de cenário pode ser interessante.
Assim como a classe CenarioSelenium, apresentada na seção anterior, a classe abstrata CenarioFragmentoSelenium é uma subclasse de BaseExecucaoSelenium. A diferença é que fragmentos de cenário compõem cenários mais complexos. Ou seja, é possível criar fragmentos de cenário dentro de um cenário mais complexo, como é o caso do cadastro de processo.
De outro ponto de vista, fragmentos de cenário são cenários simplificados, que não têm métodos pré teste ou pós teste: possui simplesmente o método executar(), responsável pelo preenchimento da página HTML.
Fragmentos de cenário é também uma forma de reuso de código por composição. Para exemplificar este reuso pode-se tomar como base o cadastro de um processo. Na página de cadastro é preenchido um formulário que possui diversas abas. Cada uma destas abas pode ser (e recomenda-se que seja) programada como um fragmento de cenário. A primeira vantagem é dividir um algoritmo grande em partes menores e melhor gerenciáveis, além de prover reuso de código.
A aba em que é preenchido e assinado o documento da petição é um exemplo de fragmento que pode ser reutilizado em diferentes situações. Além do cadastro do processo ele pode ser utilizado no envio de petições. Tem-se aqui a vantagem de reaproveitamento do código.
Com esta abordagem, para a criação de fragmentos de cenário basta que a classe de execução estenda a classe CenarioFragmentoSelenium, com o cuidado de que os métodos de pré e pós teste devem ser implementados na classe de cenário (filhas de CenarioSelenium), cujos objetos agregarão objetos da classe CenarioFragmentoSelenium.
[editar] Classe GerenciadorResultadoSelenium
GerenciadorResultadoSelenium é uma subclasse de GerenciadorResultadoBase responsável por manipular os resultados esperados e resultados encontrados de cenários executados sobre o Selenium. Classes gerenciadoras de resultados devem sobrescrever os métodos carregarResultadosEsperados() e carregarResultadosEncontrados().
[editar] Resultados esperados
O método carregarResultadosEsperados() é executado antes da execução dos cenários, e é responsável por fazer a leitura do arquivo XML de dados em busca de uma tag com o nome resultadosEsperados. Dentro desta tag são declaradas outras tags cujos nomes representam subclasses de ResultadoBase. A figura abaixo mostra um exemplo de declaração de resultado esperado.
Dentro da tag resultadosEsperados há a declaração de uma tag ProcessoConsulta. Neste exemplo há apenas uma tag declarada dentro de resultadosEsperados, contudo, poderia haver tantas quantas fossem os resultados esperados da execução do cenário.
De acordo com a declaração acima, o framework espera que esteja implementada uma classe chamada ProcessoConsulta, subclasse de ResultadoBase. Ainda, a classe ProcessoConsulta deve possuir todos os atributos definidos dentro da tag, exatamente com os nomes apresentados na imagem. Além disso, devem haver os métodos de acesso get e set para dada um dos atributos. Esta regra é inviolável, portanto, se não for observada o gerenciamento de resultados funcionará de maneira inesperada.
A declaração ilustrada na imagem fará com que o framework instancie um objeto da classe ProcessoConsulta e invoque os métodos get e set de cada atributo, preenchendo-os com os valores declarados no arquivo XML.
É comum que dentro da tag resultadosEsperados haja uma ou mais declarações da mesma classe (neste exemplo a classe é ProcessoConsulta). Entretanto, nada impede de haver declarações de mais de uma classe na tag resultadosEsperados. O framework irá criar uma lista de objetos esperados para cada uma das classes declaradas em resultadosEsperados.
[editar] Resultados encontrados
O método carregarResultadosEncontrados() é responsável por verificar, após a execução dos cenários, os resultados produzidos pela execução. Os resultados encontrados podem ser apresentados de diversas formas, como tabelas, listas ou texto puro. Por conta desta diversidade, cada sistema que for utilizar o núcleo Selenium deve implementar sua lógica de carga de resultados encontrados. É possível, inclusive, que diferentes consultas gerem resultados de diferentes formas, o que obrigará a implementação de diferentes classes concretas para gerenciar os resultados encontrados.
Neste contexto, carregar os resultados encontrados envolve percorrer a lista de elementos HTML com valores retornardos de uma consulta, criar objetos da respectiva classe (ProcessoConsulta no exemplo acima) e adicioná-los na lista gerenciada pelo objeto GerenciadorResultadoSelenium através da chamada do método addResultadoEncontrado() da superclasse GerenciadorResultadoBase.
Este trabalho pode ser bastante repetitivo e desgastante, especialmente nos casos de sistemas em que o resultado das consultas são padronizados em elementos comuns como tabelas, por exemplo. Para reduzir a sobrecarga com programação quase sempre repetitiva o núcleo Selenium disponibiliza uma implementação padrão para o método carregarResultadosEncontrados() que procura carregar automaticamente os resultados encontrados quando estes são apresentados em uma tabela. Esta implementação é apresentada na seção seguinte.
[editar] Template de resultados
Resultados gerados pela execução de cenários de consulta em geral são apresentados em uma tabela, com linhas (elemento HTML tr) representando os registros retornados e as colunas (elemento HTML td) representado os atributos dos registros. Se a tabela sempre fosse montada desta forma seria possível sem maiores problemas criar um algoritmo para fazer a carga destes resultados. O problema é que isso nem sempre acontece. No PJe, por exemplo, o valor dos atributos não estão diretamente dentro de um elemento td. Muitas vezes estão dentro um elemento span, que por sua vez pode estar dentro de um div, e este sim está dentro do td.
Esta ausência de padronização faz com que seja necessária a definição de um template para cara tipo de resultado encontrado. Este template descreve como os resultados encontrados são apresentados na tabela, além de definir também a forma de encontrar a tabela por meio do xpath Selenium. Esta lógica é implementada pelo conjunto de classes ilustrados na imagem abaixo.
Para compreender o trabalho realizado pelas classes acima é necessário conhecer a forma de definição do template. O trecho de código abaixo apresenta um exemplo de template definido para os resultados esperados da pesquisa processual.
Importante observar que a tag resultadosEsperados apresentada na imagem acima é a mesma apresentada anteriormente. A diferença é que nesta última imagem está definido o template para o framework encontrar os valores na página que apresenta os resultados encontrados. O template possui basicamente três tipos de informação importantes para encontrar os resultados:
- o atributo xPathRoot na definição no elemento ProcessoConsulta: este atributo é autodescritivo. Ele instrui o framework a encontrar a tabela em que os resultados encontrados são apresentados.
- o atributo deslocamentoColuna dentro de cada tag filha de ProcessoConsulta: este atributo diz ao framework como encontrar o respectivo atributo dentro de um elemento td. Por exemplo, o valor /span do atributo deslocamentoColuna da tag numeroProcesso indica que o número do processo estará dentro de um elemento span. Quando o valor do atributo deslocamentoColuna for vazio significa que o atributo encontrado está diretamente dentro de um elemento td.
- o atributo seq dentro de cada tag filha de ProcessoConsulta: este valor indica em qual coluna o atributo encontrado está. Por exemplo, o valor 2 do atributo seq da tag numeroProcesso indica que o número do processo estará dentro do segundo elemento td da tabela que apresenta os processos listados.
Vale ressaltar que, no exemplo acima, caso haja mais de uma declaração de ProcessoConsulta, somente em uma delas é necessário definir o template, pois esta definição serve para todos os resultados encontrados. Entretanto, caso haja definição de diferentes classes em resultadosEsperados, para cada uma delas é necessário definir o template uma vez, para instruir o framework a encontrar cada um dos diferentes tipos de resultados.
[editar] ESTRUTURA PARA O PJe
Sobre o núcleo Selenium foi construída uma infraestrutura para facilitar o desenvolvimento dos casos de testes específicos para o PJe. Com isso, quando o desenvolvedor for programar novos casos de teste para o PJe ele deve estender as classes desta infraestrutura, e não diretamente as classes do núcleo genérico ou do núcleo Selenium. Isso porque muitos dos métodos abstratos são implementados nesta infraestrutura, que forma um novo framework sobre o núcleo Selenium. São implementações padrão que atende à forma de desenvolvimento das páginas do PJe. A figura abaixo apresenta o diagrama de classes do núcleo PJe do framework.
O diagrama implementado pela figura não representa exatamente a estrutura de classes do framework. O objetivo é apenas exemplificar de que forma devem ser realizadas as extensões das superclasses existentes. As classes coloridas em amarelo fazem parte o framework de desenvolvimento para o PJe. Já aquelas coloridas em azul são exemplos de classes concretas para implementação de casos de testes.
Foi desenvolvida uma estrutura para facilitar a implementação de testes automatizados nas páginas de CRUD do sistema. A classe abstrata CenarioPJeCadastroAlteracaoExclusao deve ser estendida para implementação de cenários cadastro, alteração e exclusão de registros. Por sua vez, as classes abstratas de dados DadosCadastro, DadosAlteracao e DadosExclusao devem ser utilizada para armzenamento dos dados de testes destes tipos de cenários. Apesar do acrônimo CRUD, os casos de teste de consulta não devem ser realizadas sobre esta estrutura. Mais adiantes é apresentada a classe abstrata CenarioPJeConsulta, que deve ser estendida para casos de teste de consulta.
A classe concreta InativacaoRegistro é utilizada para inativar registros diretamente na tela de consulta. Esta funcionalidade eventualmente envolve a execução prévia de um cenário de consulta do registro que será inativado, para que a lista de registros apresente na página apenas aquele que deve ser inativado. Como a inativação de registros é uma ação de comportamento padronizado em todo o PJe é possível realizar esta ação com apenas uma classe concreta, para qualquer tipo de registro que tenha este comportamento padrão. Desta forma, os diferentes cenários deste caso de uso representam os diferentes tipos de registros (diferentes páginas) que são inativados.
Os cenários de consulta não são realizados sobre a estrutura de classes CRUD apresentada previamente. Como a execução de consultas envolve a conferência de um ou mais registros, o núcleo PJe fornece a classe abstrata CenarioPJeConsulta para esta finalidade. Desta forma, todas as classes de casos de teste de consulta que forem desenvolvolvidos sobre o framework devem estender a superclasse CenarioPJeConsulta, pois ela fornece a infraestrutura para comparação entre os resultados esperados e os encontrados. Já a classe de dados para execução de consultas podem estender diretamente a classe DadosBase.
Outros cenários mais complexos que não representem simples CRUDs devem estender diretamente as classes CenarioPJe ou CenarioPJeDecorator, dependendo da finalidade.
[editar] ESTRUTURA DOS ARQUIVOS XML
O framework desenvolvido depende da existência de arquivos XML específicos para encontrar e executar casos de teste. São definidos dois tipos de arquivo: arquivos indexadores e arquivos de casos de teste. Arquivos indexadores são opcionais, entretanto, este documento foca na sua utilização por questão de organização da estrura de casos de teste.
[editar] Arquivos XML indexadores
Arquivos XML indexadores são utilizados pelo framework para indicar quais cenários de quais casos de testes são carregados e executados. A figura seguinte apresenta a estrutura de um arquivo indexador à esquerda, e os dois arquivos de casos de teste à direita. As cores indicam o relacionamento entre o arquivo indexador e os arquivos de casos de teste.
A seguir são apresentadas as funções de cada um dos atributos do elemento raiz TestCase:
- ativo (boolean): indica se o caso de teste deve ser executado. Caso o valor seja false o objeto de indexação de cenários não terá seu método executar() invocado pelo framework, portanto, os cenários descritos no arquivo não serão executados. Na ausência do atributo o framework considera o valor como true.
- recarregarPagina (boolean): indica se a página deve ser recarregada (refresh) antes de iniciar a execução dos cenários descritos no arquivo. Na ausência do atributo o framework considera o valor como false.
- fecharNavegador (boolean): infica se o navegador deve ser fechado após a execução dos cenários descritos no arquivo. Na ausência do atributo o framework considera o valor como false.
- iteracoes (int): indica quantas vezes os cenários descritos devem ser executados. Na ausência do atributo o framework considera o valor como 1.
- maximizarNavegador (boolean): indica se o navegador deve ser maximizado antes de iniciar a execução dos cenários descritos no arquivo. Na ausência do atributo o framework considera o valor como false.
- efetuarLogoff (boolean): indica se deve ser feito logoff no sistema após a execução dos cenários descritos no arquivo. Na ausência do atributo o framework considera o valor como false.
- url (String): indica a url que deve ser acessada para executar o caso de teste. Se não for informado um valor é usada a definição do arquivo test.properties.
- nomePlanoTeste (String): indica o nome do plano de teste do Test Link usado na intergração. Se não for informado o atributo o framework não enviará para o Test Link o resultado da execução dos blocos lógicos.
Conforme descrito, o arquivo indexador é usado para listar os cenários que serão executados. Estes cenários são organizados em blocos lógicos, conforme ilustrado à esquerda entre as linhas 14 e 20 da imagem. Um arquivo indexador pode conter vários blocos lógicos dependendo da necessidade de teste. Dentro de um bloco lógico pode haver tantos cenários quanto necessário para completar um cenário de teste desejado. O nó blocoLogico possui os seguintes atributos a conhecer:
- ativo (boolean): indica se o bloco lógico deve ser executado. Em arquivos indexadores com muitos blocos lógicos eventualmente é necessário desativar algum. Este atributo pode ser usado para esta finalidade. Na ausência do atributo o framework considera o valor como true.
- nome (String): nome do bloco lógico usado na geração de log. É importante que o nome guarde relacionamento com a finalidade do bloco lógico. Se o atributo não for informado o framework irá gerar um nome genérico sequencial para o bloco lógico.
- testlink (String): número do caso de teste no testlink usado para integração. Após a execução do bloco lógico o framework gera no Test Link um log com o resultado da execução. Na ausência do atributo o framework deixará de enviar para o Test Link o resultado da execução do bloco lógico.
- gerarEvidencia (boolean): indica se devem ser geradas evidências de execução para os cenários contidos no bloco lógico. Na ausência do atributo o framework considera o valor como true.
Blocos lógicos são formados por cenários de execução. Um bloco lógico pode conter um ou mais cenários, dependendo da necessidade de teste. à esquerda da imagem, nas linhas 16 e 18 são apresentadas duas definições de cenários de execução, cujos atributos são:
- ativo (boolean): indica se o cenário deve ser executado. Na ausência do atributo o framework considera o valor como true.
- nome (String): nome do cenário que deve ser executado. O framework utiliza este nome para encontrar a classe cujo objeto será instanciado para execução do cenário. Este nome também é usado para encontrar a classe de dados que armazena os dados para execução. No caso do cenário definido na linha 16, por exemplo, o framework instanciará um objeto da classe Login para execução do cenário e um objeto da classe DadosLogin para armazenamento dos dados de execução. Os dados para execução deste cenário são encontrados no arquivo Login.xml. A ausência do atributo gera uma exceção e o cenário de teste não é executado.
- ref (int): indica qual cenário do arquivo de caso de teste será executado. No exemplo da linha 16 o framework procurará no arquivo Login.xml pela definição de um cenário cujo atributo id tenha o mesmo valor que atributo ref, conforme destacado pelas anotações em vermelho à direita da imagem. Na ausência do atributo é lançada uma exceção e o cenário não é executado.
- gerarEvidencia (boolean): indica se o cenário deve geradar evidências de sua execução. Na ausência do atributo o framework considera o valor como true.
[editar] Arquivos XML de cenários
Arquivos XML de cenários contêm um ou mais cenários de testes para serem executados. Um arquivo XML que contém os cenários para execução é chamado de arquivo de caso de teste. Estes arquivos têm o mesmo nome das classes de execução de cenários, conforme definições já apresentadas. Diferentes cenários representam diferentes formas de executar um caso de teste com diferentes dados. A figura abaixo ilustra a definição de um arquivo de caso de teste para logon de usuário no PJe. O nome do arquivo é Login.xml.
Um arquivo de caso de teste é formado por uma ou mais definições do nó cenario. A seguir é apresentada a descrição de cada um dos atributos do nó cenario:
- id (int): este atributo identifica unicamente os cenários, e é usado no arquivo indexador para referenciar os cenários que serão executados. A ausência do atributo fará com que o cenário não seja encontrado por arquivos indexadores e, por conseguinte, não seja executado.
- nome (String): nome do cenário. Representa o nome da classe de execução do cenário. No exemplo da imagem, o nome da classe de execução é Login, e o nome da classe de dados é DadosLogin. A ausência do atributo lançará uma exceção e o cenário não será executado.
- ativo (boolean): indica se o cenário deve ser executado. Na ausência do atributo o framework considera o valor como true.
- recarregarPagina (boolean): indica se a página deve ser recarregada (refresh) antes de iniciar a execução do cenário. Na ausência do atributo o framework considera o valor como false
- fecharNavegador (boolean): infica se o navegador deve ser fechado após a execução do cenário. Na ausência do atributo o framework considera o valor como false
- efetuarLogoff (boolean): indica se deve ser feito logoff no sistema após a execução do cenário. Na ausência do atributo o framework considera o valor como false
- url (String): indica a url que deve ser acessada para executar o cenário. Se não for informado um valor é usada a definição do arquivo indexador. Se este último não estiver definido é usado o padrão definido no arquivo test.properties.
As declarações dentro do nó cenario são livres, contudo, não pode haver elementos vazios. Três tipos de declarações são esperadas dentro do nó cenário:
- declarações de resultados esperados da execução de cenários: esta declaração é identificada pela presença do elemento resultadosEsperados.
- declarações de resultados da execução de cenários: identificada pela presença do elemento resultados.
- declarações de atributos e seus valores: todos os demais nomes de elementos, em princípio, devem ser considerados atributos que serão utilziados pela classe de dados de execução.
[editar] Resultados de cenários
O conceito de resultados de cenários foi previamente apresentado na seção que descreve a interface IResultadoCenario. Deve-se ter o cuidado de não confundir o conceito de resultado com os conceitos de resultados esperados e resultados encontrados.
Resultado de cenário é um conjunto de valores produzidos pela execução do cenário. Estes cenários podem ser de CRUD ou qualquer outra operação de sistema. O distribuição de processo é um exemplo de cenário que gera como resultado o número do processo. Este recurso foi criado para que cenários possam compartilhar valores com outros cenários executados posteriormente. A imagem abaixo mostra um trecho do arquivo CadastroProcesso.xml com a definição de resultados de cenário.
Entre as linhas 39 e 42 é apresentada a definição de resultados de cenário. Dentro da tag resultados (no plural) devem ser declaradas uma ou mais tags resultado (no singular). Dentro das tags resultado devem ser informados nomes de atributos declarados na classe de dados de cadastro de processo. Ou seja, a classe DadosCadastroProcesos deve possuir os atributos numeroProcesso e cpfParteIntimacao, assim como seus métodos get e set. Com esta definição, após a execução do cenário o framework criará uma lista com o número do processo e com o CPF da parte que será intimada.
Os resultados produzidos por um cenário servem para serem utilizados pelos cenários seguintes. No exemplo da figura, qualquer cenário cuja classe de dados declare os atributos numeroProcesso ou cpfParteIntimacao poderá utilizar estes valores. Os atributos têm seus valores setados automaticamente pelo framework.
Esta é a forma tradicional de o framework gerenciar resultados produzidos pelos cenários. É possível, contudo, utilizar esta funcionalidade em situações mais complexas. Quando o cenário produz um resultado o framework armazena este resultado em um mapa de valores. O framework utiliza este mapa da seguinte forma: a chave é o nome do atributo e o valor é o próprio valor do atributo.
Entretanto, é importante salientar que se a classe DadosCadastroProcesso não possuir os atributos numeroProcesso e/ou cpfParteIntimacao o framework não conseguirá setar os valores para os resultados do cenário. Neste caso, cabe ao desenvolvedor setar o valor do resultado. Com esta estratéria, qualque valor pode ser definido manualmente pelo desenvolvedor. Poderia ser setado como resultado do cenário, por exemplo, um objeto complexo com atributos e listas de outros objetos. No exemplo da imagem, a lista de resultados esperados é usada para conferência do documento peticionado, contudo, poderia também ser setada como resultado do cenário para ser utilizada por cenários posteriores.
Mesmo que a classe DadosCadastroProcesso possua os atributos numeroProcesso e cpfParteIntimacao, os valores deles podem vir de três fontes:
- de resultados de cenários anteriores;
- da definição do arquivo XML;
- atribuição manual pelo desenvolvedor.
Pela definição do arquivo XML pode-se perceber que não é dali que os valores vêm. Também não teria sentido vir de cenários anteriores, visto que é o próprio cenário que gera o número do processo, por exemplo. Resta apenas a possibilidade de ser definido pelo próprio desenvolvedor. Tem-se então a situação em que são definidos atributos na classe DadosCadastroProcesso não para uso do próprio cenário, mas para utilização em cenários posteriores. Neste contexto, após o número do processo ser obtido na distribuição do processo ele deve ser setado para o respectivo atribudo do objeto de dados. O mesmo deve ser feito com o CPF que se deseja utilizar na intimação da parte.
[editar] Resultados esperados e resultados encontrados
O recurso de resultados esperados e resultados encontrados está documentado na seção específica sobre o assunto e na seção que descreve a classe GerenciadorResultadoSelenium.
[editar] Referência a outros cenários
O recurso de referência de cenários é uma forma de reaproveitamento de dados entre cenários. Em algumas situações pode haver inúmeros cenários de um caso de teste que diferem uns dos outros apenas por alguns atributos, tendo vários outros atributos com valores iguais. O trecho de código abaixo apresenta um exemplo de uso de referência de cenários.
Este trecho de código é do arquivo de dados do caso de teste CadastroEtnia.xml. Este caso de teste possui dois cenários (id=1 e id=2). O primeiro cenário possui dois atributos definidos: etnia e situacaoAtivo. Já o segundo cenário define apenas o atributo etnia.
Além disso o segundo cenário possui o atributo ref na definição do nó CadastroEtnia. Esta definição indica ao framework que os dados que não forem definidos no cenário devem ser copiados do cenário cujo id seja igual ao valor do atributo ref. Isso sifnifica dizer que o cenário com id=2 será executado com o valor do atributo etnia=VERDE e com o valor do atributo situacaoAtivo=true (copiado do primeiro cenário).
A utilidade do recurso fica mais evidente em cenários que possuem maior quantidade de dados para execução, como é o caso do trecho de código apresentado na figura abaixo.
Neste trecho de código do arquivo CadastroEnteAutoridade.xml é possível observar que o segundo cenário (id=2) define apenas um atributo (nomeEnteAutoridade), sendo que todos os demais são copiados do primeiro cenário (id=1) por meio da definição ref=1.
[editar] Configuração de cenários decorados
O conceito de cenários decorados está apresentado na seção específica. Entretanto, resta apresentar a estrutura de um arquivo XML que utiliza este conceito. Nunca é demais ressaltar que o objetivo do uso de cenários decorados é o reaproveitamento de código e de dados sempre que viável.
Como exemplificado nas seções anteriores, cenários de cadastro e alterações de registros no PJe são bastante semelhantes em sua forma de execução. A ideia básica é acessar um item de menu para acessar a página, preencher dados, salvar o registro e conferir a mensagem exibida pelo sistema. Em situações como esta é possível o reaproveitamento de código por herança ou por composição. O framework utiliza este último conceito para a implementação de cenários decorados, com base no padrão Decorator.
Utilizar ou não cenários decorados é uma decisão do desenvolvedor de cenários de teste, visto que o recurso sempre está disponível no framework. Esta decisão é expressada por meio de declarações no arquivo XML de dados. O trecho de código do arquivo de dados CadastroProcuradoria2G.xml apresentado abaixo apresenta a declaração de uso de cenários decorados.
O recurso de cenários decorados possui duas declarações de atributos do nó cenario. A declaração decorator=CadastroProcuradoria1G indica que o framework deve executar o algoritmo de execução do cenário CadastroProcuradoria1G. Já a declaração refDecorator=1 indica que o algoritmo deve ser executado com os dados presente no cenário com id=1 da classe de execução CadastroProcuradoria1G. Se não for definido un valor para o atributo refDecorator, ou se este atributo estiver ausente, o framework ainda executará o o algoritmo da classe CadastroProcuradoria1G, contudo, usará os dados do próprio cenário da imagem acima.
O trecho de código abaixo apresenta o arquivo XML do cenário CadastroProcuradoria1G que é referenciado pelo cenário CadastroProcuradoria2G.
[editar] ESTRUTURA PARA EXECUÇÃO AUTOMÁTICA DE AÇÕES
Esta seção descreve do ponto de vista do usuário a utilização de ações automatizadas. Do ponto de vista do desenvolvedor mantenedor do framework alguns aspectos técnicos devem ser considerados. O núcleo genérico provê um conjunto de classes que permite a execução autoḿatica de ações. Entretanto, o núcleo genérico em si não fornece qualquer ação para ser executada. A figura seguinte apresenta um diagrama completo deste projeto.
Como pode ser observado pelo diagrama, a estrutura de gerenciamento de ações é fornecida pela classe de dados, visto que as ações são configuradas no próprio arquivo xml, que é carregado pela classe de dados. Contudo, o núcleo genérico, além de não fornecer as próŕias ações, também não fornece qualque implementação para carregamento das ações: as subclasses devem implementar estas lógicas.
Na implementação atual do framework, as lógicas de carga de ações (leitura do arquivo xml) e o comportamento das ações são disponibilizados pelo núcleo Selenium (classes de cor laranja no diagrama acima). A carga das ações obedece uma lógica adequada para reconhecer a estrutura de declarações contida nesta seção. Caso outra lógica seja necessária, deve ser criada outra classe no núcleo Selenium que estenda a superclasse CarregadorAcoesBase. Esta classe deve ser informada no arquivo de test.properties para o framework saber qual classe deve instanciar para realizar a carga das ações.
[editar] REFERÊNCIAS
Artigo sobre testes funcionais automatizados apresentado no PLoP 2014: https://docs.google.com/document/d/1agC_bLN2wEEIj3uLqzEIcwQibhX7dA49gxKS0Bj6E7Y/edit
Modelador UML utilizado no projeto: http://astah.net/editions/community
Frameworks: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.29.6157&rep=rep1&type=pdf
Selenium Webdriver: http://www.seleniumhq.org/projects/webdriver
Fluentlenium: https://github.com/FluentLenium/FluentLenium
Design Pattern Page Object: https://code.google.com/p/selenium/wiki/PageObjects
Design Pattern Decorator: http://www.dofactory.com/net/decorator-design-pattern
Design Pattern Template Method: http://www.dofactory.com/net/template-method-design-pattern
Design Patterm Singleton: http://www.dofactory.com/net/singleton-design-pattern
Dutos e filtros: http://pt.wikipedia.org/wiki/Pipes_e_filtros
Composição versus herança para reuso de código: http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html
Integração do Selenium com Testlink: http://eliasnogueira.com/integracao-selenium-e-testlink