Protractor na prática em 3 passos
Não é só com teoria que se aprende automação de testes (assim como várias práticas de desenvolvimento). Por isso neste artigo vamos utilizar uma abordagem prática de como utilizar Protractor. Seguindo os 3 passos abaixo, você já saberá o básico para avançar na criação dos seus testes sozinho.
Passo 1 - Instalando
Um pré-requisito para o uso do Protractor é o Node.js, pois o framework de testes é um pacote node que deve ser instalado através NPM (Node Package Manager).
Com apenas um simples comando "npm install protractor -g" o protractor estará instalado em sua máquina e pronto para ser executado através do comando "protractor". Porém ao executar este comando o protractor vai emitir a mensagem de que exige um parâmetro para a execução do Protractor.
Esse parâmetro exigido pelo Protractor é o caminho do arquivo de configuração do mesmo, pois para executar os testes é necessário ter um arquivo de configuração que vai guiar o Protractor em como ele deve ser executado.
Passo 2 - Configurando
Existem vários parâmetros que permitem você configurar o Protractor. Abaixo eu descrevo alguns que penso serem os mais importantes.
- SeleniumAddress: Permite informar uma URL do Selenium Server que o Protractor usará para executar os testes. Nesse caso o Selenium Server deve estar previamente iniciado antes de rodar os testes no Protractor.
- SeleniumServerJar: Permite informar o caminho do arquivo .jar do SeleniumServer. Caso este parâmetro seja utilizado, o Protractor irá controlar a inicialização e a finalização do Selenium Server, não sendo necessário iniciar o Selenium Server antes de executar o Protractor.
- SauceUser e SauceKey: Caso informados o Protractor irá ignorar o parâmetro SeleniumServerJar e irá executar os testes contra um Selenium Server no SauceLabs.
- Specs: Um array de arquivos de testes podem ser passados através do parâmetro "specs" os quais o Protractor irá executar. O caminho dos arquivos de teste devem ser relativos ao arquivo de configuração.
- seleniumArgs: Permite passar parâmetros para Selenium caso o Protractor inicialize-o através do "SeleniumServerJar".
- capabilities: Parâmetros também podem ser passados para o WebDriver através do "capabilities", onde é informado browser contra qual o Protractor vai executar os testes.
- baseUrl: Uma URL de acesso padrão pode ser passada para o Protractor através do parâmetro "baseUrl". Com isso toda a chamada feita para um browser será para essa url especificada.
- framework: O framework de testes e de assertions que será utilizado pelo Protractor pode ser determinado pelo parâmetro "framework". Para este existem as seguintes opções: Jasmine, Cucumber e Mocha.
- allScriptsTimeout: Para configurar um timeout para cada teste executado o Protractor provê o parâmetro "allScriptsTimeout" o qual deve receber um valor em milisegundos.
Todos esses parâmetros são encapsulados em um objeto node com nome de config para que o Protractor possa identificar esses parâmetros.
No código abaixo temos um exemplo de arquivo de configuração do Protractor o qual foi salvo como config.js.
exports.config = { seleniumServerJar: './node_modules/protractor/selenium/selenium-server-standalone-2.39.0.jar', specs: [ 'tests/hello_world.js' ], seleniumArgs: ['-browserTimeout=60'], capabitilies: { 'browserName': 'chrome' }, baseUrl: 'http://localhost:8000', allScriptsTimeout: 30000 };
Após ter o arquivo de configuração pronto, basta ir até a pasta do arquivo e executar o comando protractor config.js e o Protractor irá executar seguindo as intruções passadas no arquivo. Porém a mensagem abaixo será exibida devido a não termos um arquivo de testes chamado hello_world.js ainda.
/usr/local/lib/node_modules/protractor/lib/cli.js:91 throw new Error('Test file ' + specs[i] + ' did not match any files.'); ^ Error: Test file tests/hello_world.js did not match any files. at run (/usr/local/lib/node_modules/protractor/lib/cli.js:91:13) at Object.(/usr/local/lib/node_modules/protractor/lib/cli.js:265:1) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:901:3
Então vamos criar os testes para que o Protractor possa executa-los.
Passo 3 - Criando testes
Como já citado nesta introdução o Protractor roda em cima do Selenium e por isso ele se utiliza de todas as vantagens que o Selenium tem embutido em si. Porém o que faz do Protractor o melhor framework de automação de testes para aplicações AngularJS são as facilidades criadas nele especificamente para testar esse tipo de aplicação.
Os comandos customizados pelo Protractor visam capturar os elementos da interface da aplicação através das diretivas do AngularJS. Isto torna o framework interessante pelo fato de que o profissional que aprender a utilizar o Protractor, automaticamente vai aprender sobre o comportamento do AngularJS em relação a renderização de elementos na interface. O inverso também acontece, a partir do momento que se conhece AngularJS, facilmente se aprende a utilizar o Protractor.
O AngularJS utiliza algumas técnicas específicas para manipular o DOM, inserindo ou extraindo informações do HTML. Exemplos disso são a utilização do ng-model para a entrada de dados, o binding de atributos para exibir dados no DOM e o ng-repeat para a exibição de informações no HTML que estejam contidas em uma lista no javascript.
Para capturar um elemento na interface que contenha um ng-model por exemplo o Protractor provê o comando by.model("nome"). Através desse comando o framework retorna o WebElement o qual contém a diretiva ng-model com o valor nome.
<div> <label>Nome</label> <input type="text" ng-model="nome"> <label>Endereço</label> <input type="text" ng-model="endereco"> </div>
Dado o exemplo acima, quando executado o comando element(by.model("nome")) o Protractor retorna o WebElement abaixo:
<input type="text" ng-model="nome">
Para capturar elementos os quais o AngularJS faz binding de alguma informação o comando é um pouco diferente, mas a lógica é a mesma do model. O exemplo abaixo demonstra como utiliza-lo.
<div> <label>{{nome}}</label> <label>{{endereco}}</label> <label>{{CEP}}</label> </div>
Acima temos um código onde fizemos alguns bindings de informações através do AngularJS. Ao executar o comando element(by.binding("endereco")) pelo Protractor, é retornado o WebElement abaixo:
<label>{{endereco}}</label>
Na utilização do ng-repeat o Protractor fornece o comando by.repeater("aluno in alunos") o qual retorna um array de elementos.
Este comando permite encadear as chamadas das funções row e column para tornar sua busca mais especifica.
Encadeado com o comando row retorna o elemento em que o ng-repeat está porém com as informações do objeto na posição passado por parâmetro ao comando row. Ex: by.repeater("aluno in alunos").row(0) retorna o primeiro elemento da lista de nomes, pois o javascript inicia a contagem do 0.
Caso queira buscar um elemento específico dentro da estrutura do repeater, é possível utilizar ainda o comando column encadeado com os outros dois anteriores. Neste comando passa-se o nome do atributo que está sendo feito binding no HTML, com isso o Protractor retornará o WebElement no qual este binding está sendo feito.
O exemplo abaixo demonstra como funciona estes comandos.
<div ng-repeat="aluno in alunos"> <span>{{aluno.nome}}</span> <span>Nota {{aluno.nota}}</span> <input type="text" ng-model="aluno.nota"/> </div>
<div ng-repeat="aluno in alunos"> <span>André</span> <span>Nota 8</span> <input type="text" ng-model="aluno.nota"/> </div> <div ng-repeat="aluno in alunos"> <span>Fernando</span> <span>Nota 9</span> <input type="text" ng-model="aluno.nota"/> </div> <div ng-repeat="aluno in alunos"> <span>José</span> <span>Nota 7</span> <input type="text" ng-model="aluno.nota"/> </div>
Dado este exemplo podemos entender como funciona o comando by.repeater do Protractor.
No caso de executado o comando element(by.repeater("aluno in alunos")) o Protractor irá retornar toda a estrutura HTML apresentada a cima.
Executando o comando element(by.repeater("aluno in alunos").row(1)) é retornado apenas a estrutura de HTML abaixo:
<div ng-repeat="aluno in alunos"> <span>Fernando</span> <span>Nota 9</span> <input type="text" ng-model="aluno.nota"/> </div>
E no caso mais específico para buscar um elemento final, executando o comando element(by.repeater("aluno in alunos").row(1).column("nota")) o Protractor retorna apenas a tag span abaixo:
<span>Nota 9</span>
Agora que já sabemos como usar alguns dos comandos do Protractor, vamos criar alguns testes para demonstrar na prática o uso dos mesmo. Vamos utilizar o exemplos de site do AngularJS (http://angularjs.org/) o que apresento abaixo.
<html ng-app> <head> </head> <body> <div> <label>Name:</label> <input type="text" ng-model="yourName" placeholder="Enter a name here"> <hr> <h1>Hello {{yourName}}!</h1> </div> </body> </html>
Para esta aplicação podemos criar 2 testes como exemplo:
- No primeiro vamos verificar se o texto inicial da tag h1 é "Hello !", pois não temos nenhum valor no atributo "yourName" ainda.
- Em seguida usaremos "Protractor" como entrada de dados para representar o atributo "yourName" e verificar novamente the o texto dentro da tag h1 contém o valor "Hello Protractor!". O comando sendKeys("Protractor") que é um método do WebElement para digitar o valor dentro do campo texto.
describe('Hello World form', function() { it('should display Hello !', function() { browser.get(''); expect(element(by.binding('yourName')).getText()).toEqual("Hello !"); }); it('should be able to insert text',function(){ browser.get(''); element(by.model('yourName')).sendKeys("Protractor"); expect(element(by.binding('yourName')).getText()).toEqual("Hello Protractor!"); }); });
Colocando esses testes dentro no arquivo hello_world.js conforme configuramos no capítulo anterior irá fazer desta a sua atual suite de testes.
O que você precisa fazer agora é rodar novamente o Protractor e agora não será mais uma mensagem de erro que será exibida mas o status de cada um dos testes executados.
Finished in 2.663 seconds 2 tests, 2 assertions, 0 failures
Voila! Seus testes estão funcionando. Você pode incrementar a sua suite de testes quanto for preciso ou ainda melhor, integrá-la a uma ferramenta de integração contínua para mantê-los rodando constantemente.
Aviso: As afirmações e opiniões expressas neste artigo são de responsabilidade de quem o assina, e não necessariamente refletem as posições da Thoughtworks.