Blog da Improve It

Nosso processo de integração contínua

Publicado por Vinicius Manhães Teles há aproximadamente 1 ano.

O Lucas Húngaro deixou-nos um comentário pedindo que escrevêssemos sobre nossa infra-estrutura de controle de versão, integração contínua, testes unitários e cobertura de código para projetos Rails. Ótima pergunta! Muito obrigado, Lucas.

Nós utilizamos o Subversion como repositório e optamos por um modelo de integração contínua síncrono. Isso significa que toda vez que um desenvolvedor precisa integrar, ele roda uma script do Rake que faz o update do código, executa todos os testes, verifica a cobertura de código e, se tudo estiver em ordem, faz o commit do código.

Nós trabalhamos com 100% de cobertura em todos os projetos. Para isso usamos o Rcov. O script de integração executa a análise de cobertura antes de cada commit e cancela a integração caso o desenvolvedor tenha esquecido de fazer os testes para um código recém-introduzido.

A integração síncrona obriga o desenvolvedor a aguardar a execução do script de integração, antes de partir para outra tarefa de programação. Isso é bom porque evita que a gente comece outra coisa antes de saber que fizemos corretamente a atividade corrente. Além disso, ter que esperar pela execução do script nos leva a ter atenção permanente com o tempo de execução dele. Se crescer muito, a gente o aprimora para reduzir o tempo. No geral, tentamos manter o tempo de execução sempre abaixo dos cinco minutos.

O contrário da integração síncrona é a assíncrona, que utiliza algum servidor, como um CruiseControl. Nesse caso, o desenvolver faz o commit, o qual dispara um script no servidor do CruiseControl. Ele executa todos os testes e demais verificações enquanto o desenvolvedor já passou para outra atividade. Algum tempo depois ele recebe um email ou outro tipo de notificação avisando se tiver acontecido um erro. É um modelo interessante em várias circunstâncias, mas nos projetos que fazemos tentamos evitá-lo. James Shore escreveu um excelente artigo que resume as razões para não usar o CruiseControl. Nossas razões são basicamente as mesmas.

Para os testes unitários, utilizamos o Test::Unit, naturalmente, além do Mocha para os mock objects e o Selenium on Rails para os testes de aceitação.

Tags , , ,  | 16 comentários

O que você achou? Coloque seus comentários e sugestões abaixo!

Acompanhe o RSS dessa página.

Comentários (16 até o momento)

  1. Rodrigo disse aproximadamente 2 horas depois:

    Muito interessante a estratégia de vocês. Poderiam mostrar como é essa task rake que faz isso tudo?

  2. Lucas Húngaro disse aproximadamente 3 horas depois:

    Vinícius, obrigado pelo post, está bem informativo.

    Estou tentando implantar as práticas do XP na empresa em que trabalho. Seria mais fácil ir trabalhar em uma empresa que já pratique, mas o desafio de construir tudo do zero me motiva =D.

    Parabéns pelo processo desenvolvido, me pareceu bem consistente e eficaz.

  3. Daniel Passos disse aproximadamente 3 horas depois:

    Excelente post. Bastante instrutivo para quem deseja implementar testes com rails.

  4. Eduardo Scoz disse aproximadamente 4 horas depois:

    Olá Vinicius, Legal o seu post. Gostei especialmente de saber as ferramentas pra testes que vc usa, mas eu realmente discordo totalmente do James Shore quanto a não utilizar um servidor de integração.

    Toda a idéia de um servidor de integração é pra certificar que tudo o que você está fazendo irá integrar corretamente com todas as outras partes do sistema. Isso inclui servicos externos, código sendo criado por outros desenvolvedores, requerimentos, e principalmente ambiente no qual a aplicação estará rodando.

    No momento em que você tira esse serviço, e passa a rodar tudo a partir da maquina do desenvolvedor, vc perde uma grande parte: o ambiente é diferente/errado. Isso já é suficiente pra vc não poder confiar 100% no que está no servidor.

    Outra coisa é que você acaba forçando os usuários a não salvarem código no servidor, o que é algo terrivel. Claramente, vc está dizendo que ele segue as normas, ou salva o que está fazendo. O que acabará acontecendo é que os desenvolvedores vao acabar enviando código nao claramente testado. Dum outro ponto de vista, pode-se dizer que vc nunca saberá ao certo se o que está no svn é realmente 100% testavel.

    Enquanto isso pode funcionar (e claramente funciona pra vcs), acho que vc acaba perdendo muito por causa de algo tão simples como não ter um outro ambiente só para testes.

  5. Leandro Zis disse aproximadamente 5 horas depois:

    Ola Eduardo, o James Shore não é contra usar uma maquina para integração, ele é contra usar um servidor de integração tipo o CruiseControl. Este artigo explica melhor:

  6. Vinícius Manhães Teles disse aproximadamente 6 horas depois:

    Olá, Eduardo.

    Com relação a questão do servidor, acho importante diferenciar a ação de integrar o código do desenvolvedor do ato de executar testes de integração. Os termos são parecidos, mas o significado não é o mesmo. Quando digo "integrar o código", quero dizer colocá-lo no repositório de maneira segura, com a garantia de que ele não irá quebrar algo que já estava lá. Testes de integração, por sua vez, têm a ver com testar a aplicação, em um ambiente o mais próximo possível do de produção, interagindo com outros aplicativos ou equipamentos, quando aplicável.

    O primeiro tipo de integração, ou seja, colocar no repositório, é mais seguro da forma como fazemos, em minha opinião. Porque o desenvolvedor não consegue colocar código ruim no repositório. Só se consegue fazer commit se os testes realmente executarem com perfeição. Quando digo testes, quero dizer testes de todo o aplicativo e não apenas aqueles testes que o desenvolvedor acabou de criar. Essa restrição automatizada garante que o repositório esteja sempre em um estado no qual tudo está funcionando. Nossos scripts garantem isso.

    No modelo assíncrono, o desenvolvedor pode fazer commit de qualquer coisa. O máximo que o CruiseControl vai fazer é notificar as pessoas de que algo está errado. Mas, ele não impede a entrada de código ruim no repositório. Do modo que nós fazemos, ao contrário, não se consegue colocar código ruim no repositório.

    Agora vamos a segunda questão que você levantou que é a execução de testes de integração. Nesse sentido, acho o CruiseControl muito válido sim. Inclusive existem testes desse gênero que, pela própria natureza, demandam muito tempo. Às vezes precisam ser executados de madrugada, por exemplo. Para esses casos, vejo o CruiseControl como algo bastante útil.

    Não entendi direito o que você quis dizer no parágrafo que começa com "outra coisa é que você acaba forçando os usuários a não salvarem código no servidor (...) pode-se dizer que vc nunca saberá ao certo se o que está no svn é realmente 100% testável." Seria legal se você pudesse esclarecer melhor. Em todo caso, no nosso processo, temos garantias de que o código no SVN é sempre 100% testável e testado.

    Nos nossos projetos, pensamos várias vezes em adotar o CruiseControl para fazer testes integrados, mas nunca chegamos a fazê-lo. Provavelmente porque ainda não tivemos uma necessidade tão forte nesse sentido, pela própria natureza dos projetos. Entretanto, temos clientes onde a natureza dos projetos deles torna essencial o uso do CruiseControl para testes de integração. Em alguns casos, é fundamental também para a integração contínua. É o caso, por exemplo, de um cliente que trabalha fortemente com C++. Só o processo de compilação de todo o código e suas bibliotecas já é suficientemente demorado para justificar o uso de um CruiseControl. Não é o que acontece no nosso caso particular, mas se fosse, também adotaríamos o CruiseControl na integração contínua.

    No fim das contas, tudo depende do projeto. Em todo caso, sempre que o projeto permite, preferimos fazer a integração síncrona, para não permitir a entrada de código ruim no repositório, mesmo que seja por pouco tempo. Para testes de integração, por sua vez, o uso do CruiseControl me parece uma ótima idéia, porém nem todos os projetos têm uma demanda tão forte nesse sentido.

    Grande abraço, Vinícius.

  7. Vinícius Manhães Teles disse aproximadamente 6 horas depois:

    Excelente, Leandro. O que ele descreve nesse artigo é basicamente o que nós fazemos, com duas pequenas diferenças:

    • Nós usamos o Bob Esponja ao invés daquela galinha. :-) Encontre-o nessas fotos.
    • Nosso script também testa se a cobertura do código mantém-se permanentemente nos 100%.

    Grande abraço, Vinícius

  8. Marcelo Baltar disse 1 dia depois:

    Oi Vinícius,

    Parabéns, mais um excelente post. Mas devo dizer que eu também tenho uma preferência pelos testes assíncronos (CruiseControl-like ;) ). Além das questões técnicas, já bem exploradas no seu post e em alguns comentários, esse tipo de abordagem aproxima-se mais dos conceitos pregados pelas metodologias "ágeis".

    No meu entender é mais proveitoso o desenvolvedor "commitar" seu trabalho o quanto antes (lógico, com os devidos testes implementados) e já avançar para as próximas tarefas ao invés de ser obrigado a passar por todo esse processo de integração "pré-commit". Afinal de contas, quão realmente grave/problemático seria o commit de código que quebra o que está no repositório? Pela minha experiência, isso acontece pouquíssimo, e quando acontece, graças a granularidade dos commits, pode ser facilmente corrigido.

    Muito coerente inclusive com as idéias do próprio pessoal do Ruby, ao serem confrontados com supostos "problemas" da linguagem: "ei, isso é mesmo um problema? Quantas vezes escrever tanto XML melhorou seu projeto?", e por aí vai... :)

    My 2 cents!

    Abraços!

  9. Eduardo Fiorezi disse 2 dias depois:

    Abordando um lado que ainda me gera muitas dúvidas... Vocês fazem uso do assert_select para testar os elementos html do software?

    Ou tudo isso se torna parte do Selenium on Rails? ou Testes funcionais?

    Nos projetos que estou envolvido fazemos muitos testes com assert_select nos testes funcionais... Mas tenho a impressão de que esse teste deveriam estar em outro lugar...

    Como é com vocês?

    Abraços

  10. Tapajós disse 3 dias depois:

    Oi Eduardo, tudo bom ?

    Nos nossos sistemas não usamos o assert_select. A presença de elementos na página são verificados através dos testes do Selenium.

    Nossos testes do selenium não são apenas para verificar elementos na página, ele simula a iteração de um usuário, preenchendo campos, selecionando combos e etc.

    Um abração

    Tapajós

  11. Vinícius Manhães Teles disse 6 dias depois:

    Marcelo,

    Muito obrigado por seu comentário. Ele me motivou a escrever esse outro artigo.

    Grande abraço, Vinícius.

  12. Eduardo Scoz disse 10 dias depois:

    Olá Vinicius

    Desculpe a demora em responder, eu havia perdido o link para o post.

    Entendo a diferença do que voce está querendo dizer (teste de integraçao X integraçao do código), mas não concordo com a sua opinião.

    Acho comum, e não errado, ter código quebrando unit tests no scm. De uma forma meio estranha, dá até pra triar algo positivo disso, pois assim dá pra saber que os desenvolvedores estão seguindo a idéia do TDD (criar testes primeiro, depois desenvolver o código). Acho melhor que o trabalho que os desenvolvedores estão fazendo seja salvos e testados em conjunto o quanto antes possível, e se para isso código que falha os unit tests tem que ir pro SCM, tudo bem. O importante é que os erros não permanecam no codigo por muito tempo, e que os desenvolvedores trabalhem para acabar com eles.

    Forçar os desenvolvedores em colocar somente codigo que passa todos os testes no scm é, se não impossível(posi você nunca vai poder testar o que outros desenvolvedores estao fazendo), uma grande pedida para que os testes sejam deletados/não feitos apropriadamente. Você pode até confiar nos desenvolvedores do seu grupo (e confio plenamente nos meus), mas acho melhor ter regras que incentivem o uso de testes do que regras que garantem o impossível.

    Bom, engenharia de software eh a minha segunda paixão (logo depois da minha esposa). Não leve nada de forma pessoal.

    Abraços

    Eduardo Scoz

  13. Vinícius Manhães Teles disse 10 dias depois:

    Olá, Eduardo.

    Sem problemas. Discordamos nesse ponto e não há nada de errado nisso. Só gostaria de reforçar uma coisa: o que nós fazemos não é impossível. :-) Se fosse, pode ter certeza de que não perderíamos nosso tempo. ;-)

    Há um ponto aí que talvez não esteja claro e pode estar gerando algum erro de interpretação naquilo que eu havia mencionado antes.

    Vamos supor o seguinte cenário. Há oito pessoas na equipe. Eu faço checkout do projeto às 9:00h e começo a trabalhar em uma atividade. Às 9:45h resolvo integrar o código. Acontece que outras pessoas colocaram código no repositório às 9:20h, 9:33h e 9:42h. Portanto, a versão do projeto que está no meu computador está diferente da que está no repositório, não só devido ao que acabei de implementar, mas também em função do que meus colegas colocaram lá antes de mim.

    No momento em que eu executo o script de integração síncrona, eu trago automaticamente para a minha máquina as alterações introduzidas às 9:20h, 9:33h e 9:42h. Agora, todos os testes são executados e têm que passar. Eles vão levar em conta as alterações dos meus colegas e as minhas. Se tudo estiver ok, um commit será realizado. Senão, farei os ajustes necessários.

    Portanto, a integração não está preocupada com o que está na máquina de cada pessoa. O problema é o que foi para o repositório entre o momento em que eu fiz checkout e o momento em que farei o commit. A premissa é: eu peguei o projeto funcionando, vou devolvê-lo para o repositório funcionando. Esse é o tipo de verificação que nós fazemos e, como você pode notar, não é impossível.

    Entendo seu ponto de vista. Mas, particularmente, me agrada bastante a possibilidade de fazer um checkout do projeto a qualquer momento e ter a certeza de que ele funcionará. Sei que isso não é o usual e para muitos projetos parece uma realidade distante, mas ela é perfeitamente alcançável. E, na minha opinião, faz uma tremenda diferença.

    Muio obrigado pelas opiniões.

    Grande abraço, Vinícius.

  14. Lucas Fais disse 11 dias depois:

    Vocês utilizam algum software para gerenciar atividades, lista de bugs e releases? Qual?

    Muito bom o post!

  15. Vinícius Manhães Teles disse 11 dias depois:

    Olá, Lucas.

    Não temos nenhum software para gerenciar bugs. Para nós, bug é algo que deve ser eliminado assim que é encontrado e felizmente é raro identificarmos um problema nos nosso projetos, devido a taxa elevada de testes. Quando acontece algo errado, nós atacamos o problema imediatamente ou, no máximo, colocamos um post it no quadro, sinalizado como prioritário, para atacar o bug assim que possível. Para ser honesto, nós não gostamos da idéia de uma lista de bugs. Preferimos a idéia de não ter bugs quase nunca. :-)

    Quanto às atividades, há duas situações. Nos projetos presenciais usamos apenas quadro branco e cartões. São bem mais eficazes que qualquer tipo de software. Entretanto, nos projetos distribuídos, utilizamos o Basecamp para gerenciar as atividades.

    Abraços, Vinícius Teles.

  16. Rodrigo Urubatan disse 11 dias depois:

    Amanha pela manha sera publicado no meu blog um exemplo de script do Rake que estou utilizando para integração continua sincrona em alguns projetos, mas eu utilizo o GIT para controle de versões em vez do SVN :D