Testes automatizados funcionais - passo a passo
Conteúdo |
INTRODUÇÃO
Este documento descreve uma sequência de passos padrão para a automatização de casos de teste do Pje utilizando o framework desenvolvido pelo CNJ. O objetivo é que os desenvolvedores consigam desenvolver casos de testes unicamente baseados nesta documentação.
O framework foi desenvolvido com objetivo de separar a lógica de execução dos casos de teste dos dados necessários para a execução. Para isso, foram criados três componentes básicos para a execução:
- um arquivo XML com os dados necessários para a execução;
- uma classe de dados que encapsula valores dos elementos do arquivo XML em uma estrutura de fácil manipulação pelo desenvolvedor;
- uma classe (ou mais - ver sobre fragmentos de cenários) responsável pela execução do caso de teste com base nos dados encapsulados pela classe de dados.
É importante observar que, independentemente da forma de implementação da classe de execução, obrigatoriamente devem ser criados os três artefatos descritos acima, visto que o framework está “esperando” pela existência dos três para a execução dos cenários.
A documentação completa pode ser obtida no seguinte link.
PASSO A PASSO
Este capítulo descreve uma sequência de passos sugerida para o desenvolvimento de casos de teste com base no framework criado. Trata-se apenas de uma sugestão que, com base na experiência de uso do framework, tem se mostrado eficiente para o desenvolvimento dos casos de teste do PJe.
Determinação da estrutura de dados
O primeiro passo consiste em determinar a estrutura de dados necessária para a execução do caso de teste. Numa visão similar à de um caso de uso, esta estrutura deve prever os dados necessárias para a execução de todos os cenários possíveis, tanto cenário principal, quanto cenários alternativos e de exceção.
Para isso deve-se executar manualmente o caso de teste que se pretende automatizar, de acordo com o desenho do caso de teste. Esta execução manual deve produzir uma lista de dados necessários para a execução automatizada.
A figura abaixo exibe a tela de cadastro de etnias do PJe, que é usada neste documento como exemplo para criação de um caso de teste.
Para a execução deste caso de teste são necessárias duas informações: a descrição da etnia e a situação. Com estas informações é possível partir para o segundo passo, que é a construção do arquivo XML com os dados para execução.
Criação do arquivo xml
O passo seguinte consiste em criar o arquivo XML para o armazenamento dos dados de execução. Este arquivo deve estar presente dentro do diretório /xml do projeto dos testes. A Figura seguinte ilustra um arquivo XML para o cadastro de Etnias no Pje.
O arquivo XML possui um nó raiz chamado TestCase, com alguns atributos que são apresentados em seção específica. Importante observar que o nome do nó raiz obrigatoriamente deve ser TestCase, pois este nome é esperado pelo framework. Para evitar problemas com relação à estrutura do arquivo criado, recomenda-se, quando for criado um arquivo XML para um caso de teste, que este seja duplicado a partir de um arquivo já existente, para aproveitar a estrutura já criada e testada.
O arquivo XML em questão descreve uma estrutura de um caso de teste que possui dois cenários do tipo CadastroEtnia. Importante observar que o nome do arquivo XML é CadastroEtnia.xml. Este nome deve corresponder semanticamente ao propósito do caso de teste, qual seja, o cadastro de etnias. Importante observar que os nós que descrevem cenários devem ter o mesmo nome do arquivo XML, sem a extensão .xml. Mais informações sobre a nomeclatura dos aquivos podem ser obtidas neste documento.
Os dois cenários são declarados entre as linhas 5 e 13 (cenário 1) e entre as linhas 15 e 23 (cenário 2). O nó que descreve os cenários possui alguns atributos de interesse cuja documentação está disponível pode ser acessada por aqui.
Algumas observações sobre a relação entre o arquivo XML e a página cuja execução será automatizada:
- as linhas 8 e 9 do arquivo XML descrevem os valores necessários para o preenchimento dos campos da página. Os nomes destes nós são livres, contudo, recomenda-se que correspondam semanticamente aos nomes dos campos no PJe.
- entre as linhas 10 e 12 são declarados os resultados produzidos pelo cenário, que poderão ser utilizados na execução dos cenários seguintes. Este nó é opcional. Contudo, caso exista, obrigatoriamente deve se chamar resultados. Dentro deste nó são declarados nós com o nome resultado (no singular). O valor declarado dentro de um nó resultado (ver exemplo na linha 11) deve corresponder exatamente ao nome de um atributo presente na classe de dados. Este atributo não precisa necessariamente estar presente no XML (apesar de frequentemente estar presente), mas, caso seja declarado em resultado, deve estar presente na classe de dados. Ainda, para ser utilizado pelos cenários subsequentes, os arquivos de dados destes cenários também devem declarar o atributo com o mesmo nome na classe de dados. Mais informações sobre o recurso de resultados de cenários podem ser acessadas aqui.
Criação da classe de dados
Após criado o arquivo XML deve-se criar a classe de dados para encapsulamento dos atributos declarados. Esta classe deve estar em um pacote definido de acordo com o padrão de estrutura da instituição. Entretanto, definido o pacote, este deve ser declarado no arquivo XML, no cabeçalho do caso de teste, conforme ilustrado na linha 3 do código fonte exibido na figura acima.
O nome do arquivo da classe de dados deve ser exatamente o nome do arquivo XML, precedido da palavra Dados. Com isso, para o arquivo CadastroEtnia.xml, o arquivo da classe de dados deverá ter o nome DadosCadastroEtnia. A figura seguinte exibe o código fonte da classe de dados do caso de teste de cadastro de etnia.
Algumas características da classe de dados são importantes para a execução correta do caso de teste:
- os atributos declarados nas linhas 8 e 9 deve ter exatamente os mesmos nomes dos nós declarados nas linhas 8 e 9 do arquivo XML ilustrado pela Figura acima.
- todos os atributos declarados devem possuir os métodos de acesso (get e set), pois estes serão utilizados pelo framework para a alimentação dos valores dos atributos.
- classes de dados devem sempre herdar de uma das classes de dados presentes no framework. A classe em questão herdou da classe DadosCadastro. Contudo, outras classes de dados podem herdar de qualquer classe abstrata presente na hierarquia da superclasse Dados.
- os atributos de valores atômicos declarados no arquivo XML, como é o caso daqueles dois apresentados nas linhas 8 e 9 do arquivo ilustrado pela Figura acima, são alimentados automaticamente pelo framework na classe de dados, através de reflexão. Ou seja, estes valores não precisam ser alimentados manualmente pelo desenvolvedor.
- valores que não sejam atômicos (que sejam estruturados em nós e subnós) devem ser carregados programaticamente pelo desenvolvedor, dentro do método carregarDados. Na classe ilustrada pela figura acima não houve necessidade desta implementação, visto que todos os atributos são atômicos. O método carregarDados é invocado automaticamente pelo framework (desde de que a classe herde de alguma classe da hierarquia da superclasse Dados) e receberá como parâmetro, no caso exemplificado, um nó com o nome CadastroEtnia, como aquele descrito entre as linhas 5 e 13 da do arquivo XML acima. Cabe então ao desenvolvedor alimentar manualmente eventuais atributos compostos. Recomenda-se o estudo de alguns casos de teste que tenham atributos compostos como, por exemplo, CadastroProcesso.
Criação da classe de execução
O último passo sugerido é a criação da classe que irá efetivamente executar o caso de teste. Classes de execução são o único ponto da arquitetura em que há código Selenium. Esta classe deve ser criada no mesmo pacote em que foi criada a classe de dados, e deve ter o mesmo nome desta, sem o prefixo Dados. Para o cenário de cadastro de etnias, os arquivos terão os seguintes nomes:
- arquivo XML: CadastroEtnia.xml
- classe de dados: DadosCadastroEtnia
- classe de execução: CadastroEtnia
Se não for respeitado este padrão de nomenclatura o framework não consegue instanciar dinamicamente as classes, e é lançada uma exceção do tipo ClassNofFoundException. A figura seguinte exibe o código da classe de execução do cadastro de etnia.
Algumas considerações sobre a implementação da classe são importantes. Todas as classes de execução devem herdar de alguma superclasse da hierarquia de CenarioPJe. Esta herança obrigará a implementação do método executarTeste(), que é invocado internamente pelo framework. Além da classe CenarioPJe é possível que a herança seja feita a partir de classe CenarioPJeDocorator e suas subclasses. Esta herança obrigará a implementação dos métodos preExecucaoTeste() e posExecucaoTeste(), que também são invocadas internamente pelo framework. A ordem de invocação destes três métodos, bem como de outros presentes no framework é apresentada no diagrama de sequência ilustrado pela figura abaixo.
O entendimento desta ordem de execução é importante para que a classe de execução seja programada corretamente. Por exemplo, o método executarTeste() deve conter apenas código referente a ações de preenchimento de campos. Por sua vez, o método preExecucaoTeste() deve conter código relativo a ações executadas antes do preenchimento dos campos como, por exemplo, código para aguardar a presença de algum componente da página que permita o início do preenchimento dos campos. Por fim, no método posExecucaoTeste() devem ser programadas ações a serem realizadas após o preenchimento dos campos como, por exemplo, um clique no botão de gravar o conteúdo da página.
Destaca-se no código apresentado na figura acima a instrução presente na linha 11. Este exemplo mostra como utilizar as informações presentes na classe de dados. Os valores presentes na classe de dados são povoados internamente pelo framework, conforme apresentado nesta seção.
Demais configurações do arquivo XML
Outras configurações estão disponíveis no arquivo XML, cuja documentação pode ser encontrada em seção específica, tais como referência a outros cenários, resultados de cenários, resultados esperados e resultados encontrados e, por fim, cenários decorados.
Além dos links acima, a seguir também são apresentados exemplos de uso destas funcionalidades.
Referência a outro cenário
Em algumas situações, dois ou mais cenários de um caso de teste diferem apenas em relação a poucos atributos de seus dados. Neste caso, é possível declarar os dados de um cenário e utilizar estes dados como referência para os dados de outros cenários. Esta declaração é feita por meio do atributo ref no nó do cenário, conforme exemplificado pela figura abaixo.
O atributo ref presente no cenário descrito entre as linhas 24 e 28 indica que todos os dados devem ser copiados do cenário cujo atributo id é igual a 1. O traço vermelho na figura indica a correspondência entre os cenários. Esta declaração instrui o framework a preencher o cenário 2 com todos os valores de atributos do cenário 1. Importante observar que o cenário 2 declara o atributo situacao, com valor false. Desta forma o framework irá utilizar todos os dados do cenário 1 para a execução do cenário 2, exceto o atributo situacao, que sobrescreverá o valor “herdado” do cenário 1.
Cenários decorados
Em algumas situações a execução de um determinado cenário é idêntica à execução de outro cenário, com algumas ações a mais. Este comportamento é compatível com o padrão de projeto Decorator. Este padrão permite adicionar dinamicamente funcionalidades a um objeto. O framework implementa internamente este padrão quando é declarado o atributo decorator no cenário do arquivo XML. A figura seguinte apresenta um exemplo do uso do atributo decorator.
O comportamento esperado do padrão Decorator ocorre frequentemente nos cenários de cadastro e de alteração. Uma alteração executa basicamente as mesmas ações de um cadastro, com algumas ações adicionais antes ou depois do que é executado no cadastro. A declaração do atributo decorator na linha 9 indica que o cenário AlteracaoEtnia irá decorar o cenário CadastroEtnia, e utilizará os dados do cenário cujo id é igual a 2 (atributo refDecorator). Em outras palavras, internamente, quando o framework executar o método executarTeste() do cenário AlteracaoEtnia, ele executará também o mesmo método do cenário CadastroEtnia. O atributo refDecorator pode ter dois comportamentos distintos:
- se estiver presente (como é o caso do exemplo da figura), instruirá o framework a executar o cenário decorado por meio do cenário decorador, com os dados do cenário decorado. Ou seja, o cenário AlteracaoEtnia irá executar internamente o método executarTeste() do cenário CadastroEtnia com os dados presentes no arquivo CadastroEtnia.xml no nó cujo id é igual a 2.
- se estiver ausente, instruirá o framework a executar o cenário decorado por meio do cenário decorador, com os dados do cenário decorador. Ou seja, o cenário AlteracaoEtnia irá executar internamente o método executarTeste() do cenário CadastroEtnia, porém, com os dados presentes no arquivo AlteracaoEtnia.xml.
Resultados esperados e resultados encontrados
Alguns casos de teste exigem a conferência dos resultados produzidos pela sua execução. Por exemplo, em uma pesquisa de processos, uma lista de processos é esperada após a execução do cenário. Esta lista de processos esperados pode ser declarada no nó resultadosEsperados. A figura abaixo apresenta um exemplo de uso do atributo.
O nó resultadosEsperados sempre terá como filhos outros nós que representam classes implementadas pelo programador. No arquivo em questão, a declaração presente entre as linhas 9 e 15 indica que se espera que o resultado da execução do cenário seja um registro cujos valores serão encapsulados em uma classe chamada Etnia. Esta classe, por sua vez, terá os atributos etnia, registroAtivo e situacao. Esta classe deverá declarar todos os métodos de acesso, alén de um construtor vazio e um construtor com todos os parâmetros. Além disso, todas as classes que estiverem declaradas no nó resultadosEsperados devem herdar da superclasse ResultadosBase. Esta superclasse contém uma implementação dos métodos equals(), hashCode() e toString() necessários para a execução correta do framework. A figura seguinte apresenta a implementação de uma classe de resultado esperado.
Embora a definição de um construtor com parâmetros não seja obrigatória, ela facilitará a implementação das classes de execução de consultas.
Com esta definição no arquivo XML o framework montará uma lista com os objetos esperados após a execução do cenário. Este lista será utilizada posteriormente para comparação com os resultados listados na página de execução das consulta, conforme documentação disponível nesta página.
Configurações do arquivo pom.xml
Esta seção descreve as configurações disponíveis no arquivo pom.xml para o completo funcionamento do framework. Os parâmetros de execução devem ser configurados em pares de chave e valor, abaixo do nó systemPropertyVariables do arquivo xml. A figura seguinte apresenta um exemplo de um arquivo com as configurações preenchidas.
As seguintes chaves estão disponíveis para configuração:
- xmlPath: indica o diretório em que estão armazenados os arquivos xml dos cenários. Pode ser informado um caminho absoluto ou um caminho relativo ao diretório de execução da aplicação que será testada. Se não for informado o framework considera que os arquivos xml estão no mesmo diretório de execução da aplicação.
- timeOutPadrao: intervalo de tempo, em segundos, que o framework aguarda até encontrar um elemento em uma página html. Se dentro deste intervalo de tempo o elemento não for encontrado é lançada uma exceção do tipo NoSuchElement. Se não for informado um valor para este parâmetro o framework considerará um timeout de 2 segundos.
- evidenciasSucesso: o framework permite a geração de arquivos PDF com as evidências (imagens) das execuções dos testes. Este parâmetro indica se devem ser gerados arquivos PDF com as evidências de execuções bem sucedidas dos casos de teste.
- evidenciasErro: indica se devem ser gerados arquivos PDF com as evidências de execuções mal sucedidas dos casos de teste.
- evidenciasClasseGerenciadora: o framework exige que seja implementada uma classe gerenciadora das evidências de execução. Este parâmetro indica o nome da classe, que é instanciada automaticamente durante a execução dos testes. Se este parâmetro estiver ausente ou se a classe informada não existir não serão geradas as evidências.
- evidenciasNomeProjeto: nome do projeto que é adicionado no cabeçalho do arquivo de evidências. Se o parâmetro estiver ausente é gerado automaticamente o texto "não definido. Verifique o arquivo pom.xml".
- evidenciasNomeUsuarioTeste: nome do usuário responsável pelos testes automatizados que é adicionado no rodapé do relatório de evidências. Se o parâmetro estiver ausente é gerado automaticamente o texto "não definido. Verifique o arquivo pom.xml".
- evidenciasNomeArquivoRelatorio: prefixo do nome do arquivo do relatório de evidências. O nome completo do arquivo incluir, além deste prefixo, a data e hora de geração. Se este parâmetro não for definido o nome do arquivo conterá apenas a data e hora de geração.
- evidenciasNomePasta: indica o diretório em que estão armazenados os arquivos de evidência de execução dos cenários. Pode ser informado um caminho absoluto ou um caminho relativo ao diretório de execução da aplicação que será testada. Se não for informado o framework considera que os arquivos estão no mesmo diretório de execução da aplicação.
- APITestLinkIntegrar: indica se há intergração dos testes automatizados com a ferramenta TestLink. Se o valor deste parâmetro for false nenhum dos demais parâmetros da integração com o TestLink é considerado. Se não for informado o framerwork considerará que não há integração. A documentação sobre a configuração da integração pode ser obtiva neste link
- APITestLinkUrl: indica a url da biblioteca de integração com o TestLink. Se este parâmetro estiver ausente a integração não ocorrerá.
- APITestLinkChave: a integração com o TestLink é executada a partir de uma chave gerada pela própria ferramenta para o usuário. Este link contém maiores informações sobre a geração desta chave. Se este parâmetro estiver ausente a integração não ocorrerá.
- APITestLinkNomeProjeto: nome do projeto do TestLink em que serão gravados os dados de execução dos testes. Se este parâmetro estiver ausente a integração não ocorrerá.
- APITestLinkReportarSucesso: indica se devem ser gravados no TestLink informações de execuções bem sucedidas dos testes. Se este parâmetro estiver ausente o framework considerará que não devem ser gravados dados de execuções bem sucedidas.
- APITestLinkReportarErro: indica se devem ser gravados no TestLink informações de execuções com erros. Se este parâmetro estiver ausente o framework considerará que não devem ser gravados dados de execuções com erros.
- APITestLinkAnexarEvidenciaSucesso: este parâmetro somente faz sentido se o parâmetro APITestLinkReportarSucesso estiver marcado como true. Indica se deve ser gravado no TestLink, juntamente com as informações das execuções bem sucedidas, o arquivo de evidências da execução.
- APITestLinkAnexarEvidenciaErro: este parâmetro somente faz sentido se o parâmetro APITestLinkReportarErro estiver marcado como true. Indica se deve ser gravado no TestLink, juntamente com as informações das execuções mal sucedidas, o arquivo de evidências da execução.
- logFalha: o framework gera no console da IDE uma série de logs com informações sobre a execução. Este parâmetro indica se devem ser gerados no console os logs das falhas ocorridas na execução.
- logErro: indica se devem ser gerados no console os logs dos erros ocorridas na execução.
- logWarning: indica se devem ser gerados no console os logs dos alertas ocorridas na execução.
- logInfo: indica se devem ser gerados no console os logs das informações ocorridas na execução.
- logInfo: indica se devem ser gerados no console os logs informando sucesso das execuções.
- xPathPadraoConsultas: xPath padrão da tabela de retorno dos resultados das páginas de consultas. Este atributo é equivalente ao xPathPadrao apresentado nesta seção. O framework procura primeiro pelo valor definido em xPathPadrao no arquivo xml do cenário. Se for encontrado este será o valor utilizado para encontrar a tabela com os resultados. Caso o parâmetro esteja ausente é considerado o valor presente o parâmetro xPathPadraoConsultas. Lembrando que este parâmetro é válido para todas as tabelas de resultados de consultas, enquanto aquele é válido apenas para o cenário de consulta em que foi definido.
- deslocamentoPadraoConsultas: tem a mesma funcão do atributo deslocamentoColuna definido nesta seção. O framework procura primeiro pelo valor definido em deslocamentoColuna no arquivo xml do cenário. Se for encontrado este será o valor utilizado para encontrar o valor na coluna da tabela. Caso o parâmetro esteja ausente é considerado o valor presente o parâmetro deslocamentoPadraoConsultas. Lembrando que este parâmetro é válido para todas as colunas de todas as tabelas de resultados de consultas, enquanto aquele é válido apenas para as colunas do cenário de consulta em que foram definidos.
- urlAcessoDefault: url que é usada como padrão para execução dos casos de teste. O framework procura primeiro por uma url definida no cenário de teste, conforme apresentado nesta seção. Caso não exista é procurada por uma url definida no arquivo indexador de cenários, conforme exemplificado nesta seção. Se também não for encontrada a url o framework procura pela definição urlAcessoDefault no arquivo pom.xml. Se nenhuma das três definições de url for encontrada o cenário de teste não é executado.