Integração Continua – Builds rápidos com Grids e paralelismo

Como já comentamos em um post anterior, aqui na Caelum fazemos a Integração Contínua das nossas aplicações, com a ajuda de algumas ferramentas, entre elas o Selenium, para os testes de integração das aplicações.

Um dos princípios de Integração Contínua é que, no final do processo de build, tenhamos um produto pronto pra ir pra produção. Para garantir que a aplicação está nesta situação, precisamos criar diversos tipos de testes automatizados, como os unitários e os de integração.

Testes de integração são aqueles que testam funcionalidades completas, ou seja, testam a integração de vários módulos do seu sistema para garantir que a funcionalidade desejada está completa. Em aplicações web, esse tipo de teste costuma envolver abertura de browsers e simulação da interação do usuário com o sistema.

O problema desses testes fazerem parte do build da aplicação, é que eles tendem a demorar demais à medida que a aplicação cresce, ferindo outro princípio da Integração Contínua: seu build deve ser o mais rápido possível. Não adianta nada ter um servidor de Integração Contínua, se seu build demora demais, por exemplo mais de 30 minutos. Imagine que você só descobre que a última mudança que enviou para o controle de versão (commit) quebrou alguns pontos de seu programa somente meia hora depois de iniciar uma nova tarefa. Sua mente, como desenvolvedor, já está muito longe do que executou até meia hora atrás e focada em um problema totalmente distinto.

Dez minutos é um bom limitante superior pro tempo do seu build, fazendo com que o feedback seja rápido, assim um build quebrado tende a ser corrigido imediatamente, e a aplicação a estar sempre no estado pronto pra deploy o tempo todo.

Mas como fazer um build de menos de dez minutos, quando só os meus testes de integração demoram mais de meia hora? Uma alternativa é tirá-los do processo de build, e rodá-lo só no fim do dia, ou a cada hora. O problema é que perdemos o feedback rápido dos testes de integração, caindo em uma situação ainda pior que a citada acima, com o feedback de 30 minutos.

Outra alternativa, bem mais interessante, é rodar seus testes de integração em paralelo. Aqui na Caelum usamos algumas ferramentas para conseguir rodar os testes dessa maneira. A primeira delas é o Parallel Junit, capaz de rodar em paralelo um conjunto qualquer de testes compatíveis com JUnit. Para aqueles que usam o TestNG, existe um suporte natural à essa funcionalidade, que permite somente passar parâmetros na sua tag do ant ou maven.

De qualquer maneira, não adianta rodar somente uma instância do Selenium e tentar rodar os testes em paralelo. Então usamos o Selenium Grid, que permite subir mais de uma instância do Selenium Server na mesma máquina, ou melhor ainda, em várias máquinas, deixando isso transparente para os testes que usam o Selenium. De uma maneira ainda mais emocionate, podemos levantar diversos browsers distintos em máquinas com sistemas operacionais diferentes, possibilitando rodar os testes em paralelo em ambientes como Windows e Linux.

No Selenium Grid temos duas partes importantes: O Selenium Hub, que vai responder aos comandos do selenium emitidos pela sua aplicação, e delegá-los para algum dos Remote Controls, que executarão os mesmos no selenium de verdade. Em outras palavras, o Hub funciona como um proxy para cada comando que o teste deseja executar no browser, delegando essa requisição para algum Remote Control ocioso.

Um grande problema do Selenium Grid é a sua usabilidade. Para configurá-lo, precisamos subir o Selenium Hub em uma máquina, depois ir em cada máquina que rodará os Remote Controls, e subir um por um na mão, usando uma task pronta do Ant. Chato? Mas ainda não é o maior problema…

Se a máquina que tem o Hub cair, precisamos ir em todas as máquinas que tem os Remotes, e registrar tudo novamente. Se uma máquina que tem os Remotes cair sem executar um shutdown limpo, não desregistrando seus remotes, precisamos reiniciar tudo de novo, pois se um teste tentar usar o Remote “fantasma”, vai dar erro. Por fim, se um teste abrir um Selenium, e esquecer de fechar, o Remote Control associado a esse Selenium ficará travado para sempre, e então você terá que reiniciar todos os programas novamente.

Por causa disso, eu e o Guilherme Silveira resolvemos hackear o Selenium Grid e resolver esses problemas. Criamos então um fork do projeto original no github, e um novo projeto, o Selenium Box Agent.

A idéia desse novo projeto é a seguinte: Em cada máquina onde serão rodados os Remote Controls, deixamos uma instância do Box Agent rodando. Na máquina onde rodará o Hub, rodamos o projeto do Grid modificado. E então é só ir na página de console do Hub (geralmente em http://localhost:4444/console, substituindo localhost pelo ip do seu servidor) e registrar os Box Agents, pedindo para iniciar os Remotes na mesma página, sem necessidade de linha de comando, tasks do ant, nem qualquer outro trabalho manual e repetitivo. Apenas formulários e links na página do console.

Página Principal do Hub modificado

Página Principal do Hub modificado


box adicionado

Box adicionado


Remote Control Adicionado

Remote Control Adicionado

Se a máquina do Hub cair, quando ela voltar levantará automaticamente os Remotes de todos os Boxes registrados. Se uma máquina de Box cair, o Hub desregistrará automáticamente todos os seus Remote Controls, e quando ela voltar o Hub registra todos eles automaticamente também. Se um teste esquecer de devolver um Selenium, você pode desregistrar o RemoteControl associado, e pedir pra registrá-lo novamente, direto da página de console.

Um dos nossos builds, que demorava cerca de 50 minutos quando os testes eram rodados serialmente, demora por volta de 8 minutos quando rodado em paralelo, usando 7 threads simultâneas de testes. Se a aplicação crescer mais, é só registrar mais Remote Controls e aumentar o número de Testes simultâneos. E pronto, conseguimos o build de dez minutos, sem sacrificar os testes de integração.

O resultado? Uma equipe mais propensa a corrigir seus próprios erros a medida que eles são cometidos, sem ter que esperar o feedback de um cliente, possivelmente furioso, sobre algo que foi quebrado na última entrega.

Aqui na Caelum tentamos sempre encontrar alguns projetos com os quais podemos contribuir, seja com comentários, documentação ou código e ter sido capazes de criar uma extensão para o Selenium Hub é mais uma maneira que encontramos para compartilhar algo criado por nós com aqueles que procuram e precisam de uma solução similar.

10 Comentários

  1. Tapajós 09/02/2009 at 12:29 #

    Parabéns pelo post. Realmente a integração contínua é uma as práticas mais importantes.

    Eu sou fá da síncrona, não curto muito o modelo assíncrono. Porém o mais importante é ter a integração, não interessa o tipo. 🙂

    Eu tenho evoluído o SeleniumOnRails e estou com contato direto com um desenvolvedor lá da OpenQA, que me tornou um commiter do projeto.

    O Selenium Grid também é mantido por eles? Se for podemos conversar com o Eric para incorporar o seu fork no repositório oficial.

  2. Alberto 11/02/2009 at 01:19 #

    Muito bom o post!! Acho que vai ser de grande ajuda para varias pessoas que passavam pelo mesmo problema!!!

  3. Guilherme Chapiewski 16/02/2009 at 00:06 #

    Excelente 🙂

  4. Joel Lobo 17/02/2009 at 01:50 #

    Muito bom! Integração contínua, hoje, é o basicão.

    Mas… não concordo com sua definição de Testes de Integração.

    Olha essa aqui: “Na fase de teste de integração, o objetivo é encontrar falhas provenientes da integração interna dos componentes de um sistema.”
    http://pt.wikipedia.org/wiki/Teste_de_software#Teste_de_integra.C3.A7.C3.A3o

  5. Seu Enium 16/03/2009 at 01:32 #

    Caramba….
    Achei muitio boa essa idéia… O grid tem esses problemas mesmo de gerenciamento dos nós… Até comecei a hackear o grid para fazer isso que fizeram. Tava pensando em outra solução, mas essa que vocês fizeram ficou bem melhor… Parabéns!!!

  6. Aldrin Leal 23/04/2010 at 18:09 #

    Eu resolvi assinando a saucelabs. que fornece um serviço em grid bem interessante.

    Ainda faltam uns detalhes, mas estou escrevendo alguns plugins para o Maven e o Hudson, em particular.

  7. Pedro 24/01/2013 at 16:36 #

    Muito legal o post, mas preciso de uma ajuda cara. Não consigo rodar os testes em paralelo. Meus testes estão levando 50 minutos para serem executados e preciso dar um jeito nisso urgente!
    A configuração do Selenium Grid é muito complicada. Até consegui configurá-lo porém, não consegui rodar o testes em servidores diferentes.
    Pode me dar uma ajuda?

  8. Rafael 25/04/2013 at 11:06 #

    Lucas Cavalcanti,

    ressuscitando o seu post, estou tentando usar o seu projeto no gihut, mas pelo que me parece o seu projeto é antigo e aparentemente só roda com o selenium rc, com o webdriver eu não conseguiria utilizar o seu projeto correto?

Deixe uma resposta