2
Automação de testes funcionais com o Selenium
Abstract
The software testing is fundamental in software quality assurance. The testing can be split
according with its objective. An important testing objective is the check of the product be-
havior. This kind of test is known as functional testing. However, the systematic execution
of testing requires time and effort. Many times this activity is ignored. It is important the
use os testing tools in order to automate this activity. Selenium is an example of this. It
is free of charge and it can export the generated tests for a great variety of technologies,
like PHP, Java and Ruby. This work presents the basic concept related to functional tes-
ting and describes how to use Selenium integrated to Java. The work also presents good
practices related to software testing that can increase productivity and reusability.
Resumo
2.1. Introdução
No cenário atual, em que softwares estão presentes nas diversas atividades do cotidiano
e no qual os sistemas estão cada vez mais voltados para web, a qualidade exigida pelos
clientes sobre os softwares desenvolvidos se torna cada vez maior. Uma das formas mais
utilizadas e recomendadas para se garantir a qualidade de software se dá a partir da re-
alização de testes, visto que testes podem ser usados para revelar a presença de defeitos
[Myers 2004].
O teste de software pode ser definido como a verificação dinâmica do funciona-
mento de um programa, utilizando um conjunto finito de casos de teste, adequadamente
escolhido dentro de um domínio de execução, em geral infinito, contra seu comporta-
mento esperado [Abran et al. 2004]. Assim, um teste, de maneira geral, envolve a execu-
ção de um programa com a aplicação de certos dados de entrada, examinando suas saídas,
verificando se elas combinam com o esperado e estabelecido nas suas especificações. É
importante ressaltar que os testes não garantem que o software não contém erros, pois
para isso seria necessário testar todas as entradas válidas, o que geralmente é impossível.
Ao realizarmos testes durante o desenvolvimento de software adicionamos valor
ao produto, uma vez que o teste corretamente executado tende a descobrir defeitos, que
devem ser corrigidos, aumentando assim a qualidade e confiabilidade de um sistema
[Pressman 2006]. A falta de testes pode fazer com que o software desenvolvido seja
entregue com defeitos, o que pode trazer muitos problemas, como por exemplo, prejuízos
financeiros, danos físicos e até perda de vidas humanas, além de prejudicar a imagem da
equipe desenvolvedora perante a empresa e desta para com o cliente. Além disso, o custo
de não testar é muito maior, já que a identificação de erros se torna mais difícil e onerosa
nos estágios finais do projeto e os custos para reparação crescem em uma escala elevada
com o passar do tempo [Patton 2006].
Existem vários tipos de testes, cada um voltado para um determinado objetivo.
Testes podem ser criados para verificar se as especificações funcionais estão corretamente
implementadas (teste funcional), podendo ser executados diretamente pelos usuários fi-
nais para decidir sobre a aceitação do produto desenvolvido (teste de aceitação); podem
verificar se o desempenho do software está dentro do aceitável (teste de desempenho);
se ele funciona sob condições anormais de demanda (teste de estresse); se o software é
adequado ao uso (teste de usabilidade); testes podem ter como objetivo mostrar que o
software continua funcionando após alguma alteração (teste de regressão); se os procedi-
mentos de instalação são corretos e bem documentados (teste de instalação); para verificar
o seu nível de segurança (teste de segurança); ou para verificar seu funcionamento, a partir
da liberação do produto para pequenos grupos de usuários trabalhando em um ambiente
controlado (teste alfa) ou em um ambiente não controlado (teste beta) [Neto et al. 2007].
Os testes, entretanto, requerem tempo, conhecimento, planejamento, infraestru-
tura e pessoal especializado, sendo, portanto, uma atividade onerosa [Myers 2004]. De-
pendendo do tipo de sistema a ser desenvolvido, ela pode ser responsável por mais de 50%
dos custos [Pressman 2006]. Para se ter uma idéia dos custos envolvidos, de acordo com
um relatório publicado pelo NIST [NIST 2002], U$59.500.000.000,00 é o valor relativo
ao custo de falhas em softwares desenvolvidos nos Estados Unidos, apenas em 2002. Esse
mesmo relatório estima que mais de 37% desse custo (U$22.200.000.000,00) poderia ter
sido eliminado se a infraestrutura para teste fosse melhorada.
Uma forma de reduzir os custos da atividade de testes é a partir da automação dos
testes usando alguma ferramenta apropriada. No caso dos testes funcionais, a maioria
das ferramentas disponíveis se baseia na gravação de todas as ações executadas por um
usuário, gerando um script 1 com os passos realizados. Dessa forma, as ações gravadas
podem ser facilmente re-executadas, permitindo assim a automação do teste. Além disso,
o script gerado pode ser facilmente alterado, permitindo modificações nos testes gravados
sem a necessidade de re-execução do software e nova captura [Neto et al. 2007]. Como
exemplo desse tipo de ferramenta temos o Selenium-IDE [OpenQA b], que pode ser usado
para automatizar testes funcionais em aplicações web.
Neste capítulo serão apresentados alguns conceitos relacionados aos testes de soft-
wares, em especial aos testes funcionais. Também serão apresentados os princípios gerais
acerca dos testes e a utilização da ferramenta Selenium integrado com a linguagem de
programação Java, a partir do uso do framework JUnit [Gamma and Beck ]. Além disso,
será apresentada a ferramenta Bromine [OpenQA a], que foi criada para o gerenciamento
de testes feitos com o Selenium.
• Princípio 6: Testes dependem do contexto. Os testes devem ser adaptados aos riscos
e ambientes inerentes da aplicação. Softwares de segurança crítica, como por exem-
plo o do computador de bordo de aeronaves, são testados diferentemente de soft-
wares do comércio eletrônico
• open: abre uma página usando uma URL5 que é fornecida como parâmetro;
• waitForPageToLoad : pausa uma execução do teste até que uma nova página seja
carregada. Esse comando é chamado automaticamente pelos comandos com termi-
nação “AndWait”;
2 Conjuntode testes.
3 Softwarede código aberto.
4 Linguagem de criação de scripts desenvolvida pela Netscape em 1995.
5 Acrônimo para Uniform Resource Locator.
• type: entra com um valor em um determinado campo da página;
• select: seleciona um elemento dentre uma lista de opções.
• Actions: são comandos que geralmente causam uma mudança no estado da apli-
cação. Eles representam operações realizadas pelo usuário durante a execução de
um sistema web, tais como o comando “click” que indica um clique em um deter-
minado botão ou link. A maioria desses comandos pode conter o sufixo “AndWait”,
o que indica que a ação fez uma chamada ao servidor e que o Selenium deve esperar
a página carregar para executar o próximo passo.
• Accessors: examinam o estado da aplicação e armazenam o resultado em variá-
veis, como o comando “storeTitle” que armazena o titulo da página em uma variá-
vel determinada por parâmetro. Vale notar que as variáveis criadas no Selenium
podem ser acessadas de duas formas: ${nomeDaVariável} ou javascriptstored-
Vars[’nomeDaVariável’].
6 Um conjunto de rotinas e padrões estabelecidos para a utilização das suas funcionalidades.
• Assertions: são como os Accessors, mas verificam se o estado da aplicação está con-
forme o esperado. Todas as asserções do Selenium podem ser usadas de três modos:
“assert”, “verify” e “waitFor”. Para verificar a presença de um certo texto em um
determinado local, por exemplo, pode-se usar o comando “assertText”, “verifyText”
ou “waitForText”. Com o “assert”, se a verificação falhar o teste pára, enquanto que
com o “verify” a ferramenta acusa a falha mas o teste continua executando. Já no
modo “waitFor”, que é muito útil para testar aplicações com Ajax7 , o Selenium
espera o texto aparecer para prosseguir a execução.
Figura 2.2. Página Web que servirá de base para os scripts de testes apresenta-
dos neste capítulo.
no caso de dúvidas ele pode consultar a API do Selenium na aba “Reference”, na qual a
sintaxe do comando selecionado é descrito. Além disso, a ferramenta permite inserir co-
mentários, copiar/excluir/remover comandos, executar um único comando por vez, definir
onde deve começar e parar a execução.
A localização dos elementos na página web, necessária para utilizar muitos dos
comandos do Selenium (como o “type”), pode ser feita a partir de algum identificador,
do XPath9 do elemento, ou a partir do DOM10 . No caso de um campo ou formulário,
o elemento pode ser identificado por meio do seu nome ou identificador (id). Quando
não há o atributo “name” ou “id” para o elemento desejado, este pode ser localizado
usando-se xPath que inicia com “//”. A expressão “//form[1]”, por exemplo, refere-se
ao segundo formulário da página (pois a identificação inicia do zero). Por fim, os ele-
mentos da página também podem ser localizados a partir de expressões do tipo “doc-
ument.forms[0].elements[3]”, a qual se refere ao quarto elemento presente no primeiro
formulário da página.
O Selenium-IDE também fornece meios para tornar os testes mais fáceis de en-
tender e manter. Supondo, por exemplo, que foram implementados 100 casos de testes,
e que em todos eles é necessário fazer o login para acessar o sistema, então, se para
logar for preciso preencher os campos “login” e “senha”, todos os casos de testes irão
se referir a esses dois elementos (identificados na página por “login” e “senha”). Agora,
supondo que por algum motivo, como o uso de algum framework, o nome dos campos
tenha que ser alterado para “form:login” e “form:senha”. Nesse caso, todas as referências
aos campos “login” e “senha” devem ser alterados para “form:login” e “form:senha”, re-
spectivamente. Fazer essa alteração manualmente certamente exigiria muito tempo e toda
vez que a referência a um elemento da página fosse alterada todos os testes deveriam ser
atualizados.
Agora supondo que um testador que não tenha implementado os 100 testes, men-
cionados acima, tenha que entendê-los para saber quais funcionalidade foram testadas,
então, dependendo de como for feita a referência aos elementos da página, ele poderia
perder muito tempo tentanto identificá-los. É bom lembrar que nem sempre será possível
se referir a um elemento usando algum termo que permita a sua fácil identificação. Al-
gumas vezes (e não são poucas) os elementos da página testada deverão ser localizados
pelo Selenium a partir de expressões XPath do tipo “//table[@width=100]//tr[3]/td[2]//a”,
o que dificulta a leitura do teste.
Assim, para tornar os testes mais legíveis e fáceis de atualizar, o testador pode
usar mapeamentos utilizando JSON 11 entre nomes e os elementos da página sob teste.
Isso é possível a partir do UI-Element que é um recurso suportado tanto pelo Selenium-
IDE quanto pelo Selenium-RC. A Figura 2.7 ilustra à esquerda um teste feito para a
página de cadastro de usuários (ilustrado na Figura 2.2) feito no Selenium-IDE sem uti-
lizar UI-Element e à direita o mesmo teste utilizando o UI-Element. Com base nela,
podemos observar que utilizando o mapeamento o teste fica muito mais legível e fácil
de entender. Com o script da parte B da figura é fácil perceber que o teste é feito sobre
a página de cadastro de usuário, que após a preenchimento dos campos foi pressionado
o botão “Cadastrar” e que o resultado esperado é uma mensagem com o texto “Usuário
Cadastrado”.
Outra vantagem da utilização do UI-Element é a fácil visualização da descrição
dos elementos. Esta é especificada a partir do mapeamento e é visível na aba “Refer-
ence” após clicar em algum comando que utilize um elemento previamente mapeado.
Essas descrições tornam o teste ainda mais claro, permitindo que o mesmo seja rapida-
mente compreendido. A Figura 2.8 ilustra a descrição associada ao “elemento cadas-
troUsuario::mensagemExibida”, localizado no terceiro div da página (pois seu locator é
“//div[2]”).
Para construir o mapeamento entre nomes e elementos basta usar um editor de
textos e criar um arquivo no formato “.js” com mapeamento feito usando os comandos
reconhecidos pelo UI-Element. Em seguida, esse arquivo deve ser submetido como ex-
tensão do Selenium Core12 a partir do menu “Opções” do Selenium-IDE.
Os comandos que podem ser utilizados para criar o mapeamento através do UI-
Element são:
Selenium-IDE. A Figura 2.9 ilustra o mapeamento que foi utilizado no teste exibido na
parte B da Figura 2.7. Observe que se a localização (ou forma de identificação) dos
elementos for alterada, basta atualizar o atributo “locator” para que todos os testes estejam
atualizados. Se o endereço da página de inserção de usuário for alterado, por exemplo,
então basta atualizar o atributo “pathRegex” do conjunto de página (Pageset) nomeado de
“cadastroUsuario” e o atributo locator do elemento “linkUsuario” para que todos os testes
da página de cadastro de usuário sejam atualizados.
Por fim, muitas vezes existem sequências de comandos que são executadas mais
de uma vez em um mesmo teste. Também existem sequências de comando que são exe-
cutadas em mais de teste, usando os mesmos parâmetros ou não, que podem pertencer ou
não a uma mesma suite de testes. Assim, é interessante usar algum meio para agrupar tais
sequências, possibilitando a invocação de toda a sequência com um único comando.
Com Selenium-RC, o agrupamento de comandos pode ser facilmente feito a partir
de blocos. Já para o Selenium-IDE é preciso usar o “Rollup”. A partir da definição
das regras de agrupamento (Rollup rules), o testador pode reunir comandos em grupos
possibilitando que eles sejam invocados com o comando rollup. A parte A da Figura
2.10 ilustra a definição de regras de agrupamento para reunir uma sequência de comandos
em grupo identificado por “login”, que efetua o login em uma página web. A parte B
da Figura 2.10 ilustra um teste que utiliza o comando rollup para efetuar a sequência de
comandos do grupo identificado por “login”.
Figura 2.9. Mapeamento feito para os elementos da página exibida na Figura 2.2.
A criação dos arquivos com os “Rollup rules” é feita da mesma forma que a
definição do mapeamento descrito acima, ou seja, a partir da criação de um arquivo no
formato “.js” e da submissão desse arquivo como extensão do Selenium Core a partir do
menu “Opções” do Selenium-IDE. Porém, nesse caso a primeira linha deve seguir a es-
trutura “var nomeDaVariavel = new RollupManager()” e o único comando disponível é
comando “addRollup”, usado para adicionar uma nova regra. Existem vários atributos que
podem ser definidos. No script ilustrado, o principal atributo é o getExpandedCommands
o qual especifica quais serão os comandos a serem executados.
O próximo passo é converter script do formato HTML para Java, ou seja, converter
os comandos do script nas funções disponíveis na API para a linguagem Java. Isso é feito
a partir do menu “Opções > Formato > Java (JUnit) SeleniumRC”. Caso o testador queira
executar os testes no TesteNG ele deve usar a opção “Opções > Formato > Java (TestNG)
SeleniumRC’. O resultado da exportação, para o framework JUnit, do script ilustrado na
Figura 2.11 é exibido na Figura 2.12.
Figura 2.12. Script de teste da Figura 2.11 exportado para Java (JUnit).
Os testes criados com o Selenium são testes que utilizam o JUnit, de forma similar
aos testes de unidade. Sua execução acontece da mesma forma que os testes de unidade,
clicando com o botão direito do mouse em cima da classe e solicitando sua execução via
JUnit. No entanto, existe uma diferença: para que o teste execute, é necessário que exista
uma instância do Selenium Server executando na mesma máquina que contém os testes
que serão executados. Assim, antes de executar os testes é preciso executar o arquivo
selenium-server.jar 14 . Sua execução pode ser feita via comando “java -jar selenium-
server.jar".
A Figura 2.17 exibe o resultado da execução dos testes apresentados neste capí-
tulo. O lado A da figura apresenta a primeira execução dos testes, e no lado B a re-
execução dos mesmos. Observe que no lado A da figura, a barra de execução ficou verde,
indicando que nenhuma falha foi detectada, ou seja, o teste passou. Enquanto que no
lado B, a barra de execução ficou vermelha pois o execução do último teste encontrou
uma falha. Isso porque no último teste, no qual é feito o cadastro de um novo usuário, o
resultado esperado é a mensagem “Usuário Cadastrado”. Na primeira execução da suite
de testes, eles passaram porque não havia o usuário “Usuário de teste”15 cadastrado no
sistema. Contudo, com a re-execução da mesma suite de teste, sem restaurar o banco de
dados para o estado original, o sistema não permitiu o cadastro do mesmo usuário, não
resultando, portanto, no resultado esperado pelo teste.
Vale salientar que também é possível implementar e executar casos de teste es-
critos com o Selenium utilizando a linguagem Java a partir do framework TestNG. Este
por sua vez ofecere características próprias que podem ser utilizadas para facilitar a im-
plementação dos testes. A escolha do framework a ser utilizado deve ser baseado nos
conhecimentos da equipe de teste e nos recursos que tais ferramentas oferecem.
Assim como no Selenium-IDE, o mapeamento feito com o UI-Element também
pode ser usado com o Selenium-RC, porém neste só é possível adicionar um arquivo
de mapeamento e ele deve ser chamado de “user-extensions.js” e deve estar no mesmo
diretório do “selenium-server.jar”. Com isso, basta executar o servidor com o comando
“java -jar selenium-server.jar -userExtensions user-extensions.js” para que o mapeamento
seja reconhecido e dessa forma o procedimento de cadastro de usuário, por exemplo, pode
ser escrito conforme exibido na Figura 2.18.
Figura 2.16. Classe com os casos de testes.
Figura 2.17. A) A execução dos testes não encontrou falhas. B) A execução dos
testes encontrou uma falha.
Figura 2.18. Usando mapeamento de interface de usuário com o Selenium-RC.
É no painel de controle que são definidos os nós (máquinas nas quais há uma
instância do Selenium RC executando), os projetos, os usuários e permissões, além de
outras configurações que não são descritas aqui. Vale lembrar que no caso de só a máquina
local ser utilizada para executar os testes deve-se adicionar um nó com o path sendo
127.0.0.1:4444, indicando assim que há uma instância ativa do Selenium RC na porta
4444 da máquina.
Feitas as configurações necessárias, o próximo passo é definir o planejamento dos
testes. Para isso deve-se clicar no botão do canto superior direito da página para acessar
o workspace, onde encontram-se os menus “Planning” e “TestLabs”. A partir do menu
“Planning” pode-se especificar os requisitos e os casos de testes, e os relacionamento
entre eles, ou seja, a associação entre casos de testes e o(s) requisito(s) que eles atendem.
A Figura 2.21 exibe a adição de um requisito ao projeto criado. Observe que nele é
especificado a(s) plataforma(s) onde os testes serão executados, o que vai depender da
configuração dos nós que foram adicionados.
2.8. Conclusões
A atividade de testes é essencial para garantir a qualidade dos produtos de software de-
senvolvidos. Contudo, essa é uma atividade onerosa, pois exige tempo, ferramentas apro-
priadas e pessoal especializado. Por conta disso, muitas vezes essa atividade só é possi-
bilitada a partir do uso de ferramentas que permitam a automação dos testes. No caso dos
testes funcionais para aplicações Web, a ferramenta Selenium é uma boa opção por ser
gratuita e por permitir o uso de linguagens de programação como Ruby ou Java.
Neste trabalho apresentamos os princípios e os principais conceitos ligados aos
testes de software. Além disso, foi apresentado o Selenium-IDE e foi descrito em detalhes
a automação de testes funcionais utilizando o Selenium, a linguagem de programação
Java e o framework JUnit. Por fim, foi feita uma breve descrição sobre o Bromine, uma
ferramenta que permite a distribuição da execução da suite de teste entre várias máquinas
(nós), além de facilitar o gerenciamento dos testes feitos com o Selenium.
18 Esse script deve estar no formato apropriado para ser reconhecido pelo Bromine, para detalhes deve-se
consultar o site http://wiki.openqa.org/display/BR/Creating+a+testscript.
Referências
[Abran et al. 2004] Abran, A., Moore, J., Bourque, P., Dupuis, R., and Tripp, L., editors
(2004). Guide to the Software Engineering Body of Knowledge. IEEE Computer
Society.
[Gamma and Beck ] Gamma, E. and Beck, K. Junit testing framework. Technical report,
JUnit.org, http://www.junit.org/. último acesso em 10/10/2009.
[IEEE 1990] IEEE (1990). IEEE Standard Glossary of Software Engineering Terminol-
ogy - IEEE Std.610.12-1990. IEEE Computer Society.
[Müller et al. 2007] Müller, T., Graham, D., Friedenberg, D., and Veendendal, E. V.
(2007). Base de Conhecimento para Certificação em Teste. Foundation Level Syl-
labus, 2nd edition.
[Myers 2004] Myers, G. (2004). The Art of Software Testing. John Wiley & Sons, 2nd
edition.
[Neto et al. 2007] Neto, P. S., de Sousa, F. V., and Resende, R. (2007). Automação de
Teste de Software. Escola Regional de Computação da SBC Ceará - Maranhão - Piaui,
Fortaleza.
[NIST 2002] NIST (2002). Planning report 02-3. Technical report, National Institute
of Standards and Technology. http://www.nist.gov/director/prog-ofc/report02-3.pdf.
Acessado em setembro de 2009.
[Spillner et al. 2006] Spillner, A., Linz, T., and Schaefer, H. (2006). Software Testing
Foundations. rockynook, 2nd edition.