Subm_SamuelPereira_FINAL

130 visualizações

Publicada em

0 comentários
0 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

Sem downloads
Visualizações
Visualizações totais
130
No SlideShare
0
A partir de incorporações
0
Número de incorporações
21
Ações
Compartilhamentos
0
Downloads
3
Comentários
0
Gostaram
0
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

Subm_SamuelPereira_FINAL

  1. 1. UMA ANÁLISE SOBRE O USO DE T.D.D. (TEST DRIVEN DEVELOPMENT) E INTEGRAÇÃO CONTÍNUA NO DESENVOLVIMENTO DE SOFTWARE1 Samuel Pereira de Araújo2 RESUMO Este trabalho surgiu da necessidade de realizar um estudo sobre os benefícios e dificuldades de se utilizar, em um ambiente real de desenvolvimento de software, a técnica de desenvolvimento Test Driven Development – TDD juntamente com um servidor de Integração Contínua – IC. O estudo irá levantar detalhes de configuração de ambiente de desenvolvimento, configurações do servidor de integração, IDE e controle de versão. Serão apresentadas as rotinas seguidas no decorrer do desenvolvimento de um software e as vantagens do seu uso em relação a um ambiente que não o utiliza. Também será mostrado o conceito da técnica de TDD assim como os requisitos para ter um ambiente de desenvolvimento com um servidor de integração contínua. Palavras chave: Test Driven Development; Integração Contínua; Métodos Ágeis ABSTRACT This work was originated from the need to study the benefits and difficulties of using, in an actual software development environment, Test Driven Development (TDD) together with a Continuous Integration server. This work will present the development environment, the integration server, IDE and version control configuration details. The routines followed through a software development process will be described and the benefits of using these routines will be presented. The concepts of TDD technique will also be presented as well as the requirements for a development environment that includes a continuous integration server. Keywords: Test Driven Development; Continuous Integration; Agile Methodologies 1 Trabalho apresentado à Faculdade 7 de Setembro como requisito parcial para obtenção do título de Bacharel em Sistemas de Informação. 2 Bacharel em Sistemas de Informação, graduado pela Faculdade 7 de Setembro, 2013.
  2. 2. 2 1 INTRODUÇÃO Um dos grandes desafios do desenvolvimento de software é a qualidade. Entende-se por qualidade um projeto cuja arquitetura permita um crescimento contínuo e em segurança. Uma preocupação nos projetos de desenvolvimento de software é a grande quantidade de arquivos que juntos compõem o produto final. Organizar estes arquivos demandam um tempo e esforço, no entanto podem impactar positivamente no software. Durante o desenvolvimento de um software é importante conseguir identificar bugs antes mesmo de entrar em funcionamento. Sabe-se que o conceito de um software concluído é algo abstrato, então, permitir que durante o seu desenvolvimento seja possível identificar rapidamente falhas e bugs é essencial. Dentre as definições tomadas para o ambiente no qual ficarão armazenados os códigos fontes do software, deverá ser pensada também a estratégia que será adotada para que os membros da equipe de desenvolvimento possam trabalhar em conjunto, sem que a integração de um código fonte de um membro possa interferir negativamente, sobrescrevendo o trabalho de outro ou até mesmo gerando perdas de código. Mesmo em um ambiente com um bom controle de versão, há o risco de em uma sincronização de código fonte, um membro da equipe possa interferir acidentalmente em comportamentos de funcionalidades antes esperados na aplicação, o que é difícil perceber em cenários onde não há pontos de verificações capazes de fornecer feedbacks para o desenvolvedor, como por exemplo, os testes automatizados ou testes funcionais. Após o sistema entrar em funcionamento é possível que o mesmo continue evoluindo, visto que o software deve sempre se moldar aos problemas e cenários reais do cliente, que são constantemente modificados por fatores externos. Para que isso seja possível, um investimento intelectual e de tempo deve ser feito durante a construção da arquitetura do software, no entanto muitos gestores não percebem esta estratégia como investimento, mas como um custo que poderá afetar o prazo de entrega do produto. O figura 1 apresenta um gráfico que expõe uma relação de custos ao iniciar um projeto utilizando TDD por complexidade. Nota-se que inicialmente o investimento para utilizar tais práticas junto à complexidade é alto, no entanto, no decorrer do projeto ficam perceptíveis as vantagens de se ter optado pela utilização desta técnica de desenvolvimento.
  3. 3. 3 Figura 1 – Custo do TDD por Complexidade Fonte: GARETH (2012) Na fase de publicação do software, etapas como reunir todos os códigos fontes, realizar a compilação dos mesmos e publicar o resultado da compilação junto à documentação são procedimentos rotineiros. Estas etapas são burocráticas e demandam um tempo considerável ao ser realizada em uma máquina de desenvolvimento, obrigando que a mesma necessite de todo o ambiente de desenvolvimento configurado, bem como da dependência de um membro da equipe para realizar os passos supracitados, tornando-a suscetível a reprodução de erros. I bugs - - cil de encontrar o problema. (FOWLER, Martin, Continuous Integration, p. 1-24, 2009)
  4. 4. 4 Assegurar que um software possua qualidade com o mínimo possível de falhas, erros ou comportamentos não esperados é uma tarefa bastante difícil. Com a utilização do TDD, onde o desenvolvimento do software é orientado a casos de testes, o caminho para a construção de um software consistente pode ser facilitado. Unir esta técnica de desenvolvimento a um mecanismo de Integração Contínua garantirá que toda a suíte em desenvolvimento, a cada sincronização dos fontes, terá seu ambiente estável. A utilização associada desses dois mecanismos provém qualidade ao software, agregando um aumento de produtividade e assegurando dessa forma melhorias contínuas em um ambiente estável, garantido por builds automatizados. Sendo assim, este estudo busca apresentar os benefícios e dificuldades de se utilizar TDD e IC em um ambiente real de desenvolvimento com uma equipe mista, onde apenas alguns integrantes conhecem seu uso, fomentando, adicionalmente, a qualidade do Software e a entrega real de produtos com máximo retorno para o cliente final. O principal objetivo desse trabalho é analisar o uso associado do Test Driven Development – TDD e da Integração Contínua - IC no desenvolvimento de software na plataforma .NET, evidenciando os benefícios do uso dos mesmos aplicados em um ambiente real de desenvolvimento de software através da exposição das ferramentas utilizadas no gerenciamento de builds e testes automatizados, processos da equipe de desenvolvimento e os impactos no resultado do projeto. Para fundamentar teoricamente este estudo, foi realizada inicialmente uma pesquisa bibliográfica, sobre TDD e Integração Contínua, através de consultas aos livros, artigos de periódicos, documentos eletrônicos, monografias e documentação das ferramentas utilizadas. Posteriormente foi feito um estudo de caso visando analisar a aplicação dessas metodologias/ferramentas em um ambiente real de desenvolvimento. A abordagem deste trabalho consistirá na configuração de um servidor de integração contínua utilizando Jenkins seguindo as orientações de Mark Berg (2012), em um ambiente de desenvolvimento com projetos que seguem as práticas do TDD. Sendo assim, o estudo foi desenvolvido em três etapas onde a primeira foi realizada através da referida pesquisa bibliográfica, a segunda foi a etapa de configuração do servidor de integração contínua com builds automatizados e a terceira etapa foi o acompanhamento do desenvolvimento do software procurando evidenciar os ganhos e perdas da utilização associada de TDD e IC. Por fim, o presente trabalho foi estruturado em duas seções, de forma que no primeiro consta a fundamentação teórica acerca do TDD e da IC e os pensamentos e contribuições de
  5. 5. 5 autores conceituados como Kent Beck (TDD) e Martin Fowler (IC), e no segundo, apresenta- se o estudo de caso, contextualizando sobre o ambiente e ressaltando os resultados alcançados. Espera-se com este trabalho que os benefícios da utilização do TDD associado com o uso de integração contínua sejam evidenciados e difundidos de forma que mais equipes de desenvolvimento, sejam elas entidades públicas ou privadas, possam vir a utilizar estas técnicas em seus ambientes de desenvolvimento, propiciando assim a construção de software com mais qualidade e um consequente crescimento do nível de satisfação dos usuários dos referidos software. Espera-se, ainda, que fique evidente os benefícios da utilização do TDD, associado à Integração Contínua, superam o seu alto custo na medida em que, em um curto espaço de tempo, os ganhos em produtividade e qualidade serão notórios 2 TDD Test Driven Development – TDD trata-se de uma técnica de desenvolvimento de software orientado a testes que se baseia em ciclos curtos. Segundo Beck (2002), considerado criador da técnica, para a utilização da mesma devem ser seguidos 03 passos que são denominados vermelho, verde e refatoração. No passo vermelho é escrito um teste automatizado para uma melhoria desejada ou uma nova funcionalidade com base no caso de uso ou “user stories” N passo verde é criado o código que irá fazer com o que o teste seja bem sucedido. Por fim, no passo da refatoração, na medida do possível, o código deve ser aprimorado evitando assim que os mesmos sejam duplicados e deixando apenas o que é necessário para o teste passar. Os benefícios em se utilizar TDD no desenvolvimento de software são inúmeros, como por exemplo: promover mais confiança aos desenvolvedores, visto que cada etapa é guiada a testes e a orientação do desenvolvimento do código de uma forma mais modularizada e extensível. Estes benefícios se dão em grande parte devido à técnica que foca no software em pequenas unidades. Logo, ajuda a permitir um código mais simples e limpo, melhorando consideravelmente o design do código. Boekel (2010) concorda com os benefícios da utilização a técnica ao afirmar que:
  6. 6. 6 A técnica permite que desenvolvedores, através de testes e refatorações, foquem seus esforços em pequenas partes do sistema, isolando-se do todo. Por analogia, quanto maior o problema, menor deve ser o terreno que cada teste deve abranger. Ibid. Segundo Stephens (2010), com uma grande camada de testes passando cobrindo o projeto é possível refatorar grande parte do projeto com confiança, sem medo de que sejam gerados defeitos (bugs). A figura 2 exemplifica o cenário: Figura 2: Projeto utilizando TDD e sem utilizar TDD. Fonte: HAGEMANN (2011) Segundo Feathers (2002), é possível utilizar TDD em códigos legados no sentido de melhorar códigos já existentes e também de depurá-los. Esta ideia é interessante, visto que códigos antigos podem, de uma maneira incremental, adquirir alicerces para permitir um crescimento seguro do projeto. O uso desta técnica na simulação de defeitos para correção provê uma garantia de que aquele comportamento indesejado não mais volte a ocorrer. Em um artigo, Shore (2010) expõe o porquê do TDD funciona A I E’ desenvolvimento mais completas são capazes de fornecer vários feedbacks de sintaxe e de possíveis pontos de erros ou falhos ao analisar a lógica do código, no entanto, o TDD vai fornecer em poucos segundos o retorno do resultado da execução do seu código alertando o desenvolvedor, caso o comportamento não seja o esperado.
  7. 7. 7 Existem várias discussões, como o visto em um tópico3 no “TDD no mundo real” Google Groups, onde vários desenvolvedores relatam suas opiniões sobre o uso do TDD. Percebe-se que vários são entusiastas da técnica e que possuem experiências que comprovam os ganhos de se utilizá-la. No entanto, em trechos do “não entra na cabeça deles escrever testes primeiro do código de produção”, é percebida a resistência de vários desenvolvedores para mudar a forma com que é realizada o desenvolvimento do software. Esta mudança de paradigma é um fator crucial que faz com que muitos desistam de usar esta técnica. 3 INTEGRAÇÃO CONTÍNUA O conceito de Integração Contínua, segundo Whitehead (2012), envolve um conjunto de práticas que busca facilitar e estabilizar o processo de realização de builds do software possibilitando uma série de ações automatizadas para prover uma versão do produto estável. Até que se chegue à versão estável, é possível realizar várias ações como: agendar verificações no projeto ou a cada nova modificação no código fonte para certificar que o projeto está compilando, executar os testes automatizados para avaliar se todos estão passando e por último realizar o empacotamento da versão para disponibilização. Toda esta rotina pode ter o resultado final disponibilizado para os membros da equipe e caso ocorra alguma falha em qualquer etapa a equipe deverá tomar atitudes para solucioná-lo. Mark Berg (2012) cita uma pesquisa, feita pelo NIST – Instituto Nacional de Normas e Tecnologia Americano, que evidencia que os defeitos de software geram um gasto de quase 60 bilhões de dólares por ano. Sendo assim, o autor recomenda que para possibilitar uma economia dos recursos financeiros e melhorar a qualidade do software, é preciso que os defeitos sejam removidos tão cedo quanto for possível, e que esta etapa esteja incluída no ciclo de vida do software. Com a integração contínua, a detecção de defeitos ocorre desde o começo e durante toda a construção do projeto. A utilização de um servidor de Integração Contínua exige alguns itens básicos como, por exemplo, o servidor com acesso mínimo ao código fonte, ferramentas básicas para poder realizar a compilação do projeto e também a possibilidade de realizar a execução dos testes automatizados. 3 Como convencer a equipe à usar TDD?
  8. 8. 8 A figura 3 demonstra o funcionamento de um ambiente de desenvolvimento que utiliza um servidor de Integração Contínua. Figura 3: Processo de Integração Contínua Fonte: MOREIRA, Anderson, Integração Contínua, p. 1–11, . Beck (1999), um dos criadores do Extreme Programing – XP, define como prática de integração contínua um conjunto máximo de alterações de até um dia de desenvolvimento, quando este deve ser testado por algumas horas, garantindo que todos os conflitos sejam resolvidos e que todos os testes estejam passando, para que então seja concluída a integração. Na rotina de desenvolvimento de um software, vários profissionais trabalharão simultaneamente em pequenas partes do projeto e garantir que a união dessas partes não gerem conflitos, perda de código ou um software com defeitos é de fundamental importância. A automatização desta integração irá assegurar um ganho de produtividade e qualidade ao software. Para a realização dos builds automatizados do software podem ser seguidas algumas estratégias. Kawalerowicz (2011) descreve algumas opções como: incremental/contínuo realizado frequentemente e de forma rápida, geralmente após cada sincronização dos fontes; os builds diários ou noturnos, onde é feita a compilação de toda a suíte junto a execução dos testes automatizados; e os builds semanais que seguem a mesma rotina dos builds diários.
  9. 9. 9 Dentre todas essas estratégias a mais importante é o build incremental, visto que este auxilia no decorrer do desenvolvimento do produto dando retornos para o desenvolvedor várias vezes ao dia. Normalmente, segundo Koskela (2007), o desenvolvedor, antes de realizar o commit do código fonte para o servidor, executa os testes relevantes em sua máquina para posteriormente enviar para o servidor de integração, que irá executar o build de integração, que por sua vez irá realizar os demais testes como os de integração, funcionais e unitários. O uso de testes automatizados é mais bem aplicado quando utilizada a técnica TDD. O TDD guia o desenvolvedor a produzir um código simples, com qualidade e que atende exatamente o que é preciso para a funcionalidade que está sendo desenvolvida. Então, com a execução dos testes pelo servidor de integração contínua conseguimos avaliar se toda a suíte está coesa e íntegra. Segundo Boekel (2010), o TDD ajuda desenvolvedores a trabalhar com unidades menores do problema, trabalhando diretamente com o modelo, o que faz com que seja direcionado o desenvolvimento de uma solução simples, favorecendo componentes com alta coesão e baixo acoplamento. Segundo Kawalerowicz (2011), a necessidade de se melhorar um código não ocorre apenas quando há existência de defeitos (bugs) ou quando o código não faz o que estava determinado a fazer. Códigos conhecidos como smelly code, ou seja, códigos “fedorentos”, são partes do programa que podem vir a gerar problemas, sejam eles de funcionamento do software ou dificuldades de manutenção. Uma excelente funcionalidade/ferramenta da integração contínua que pode ser utilizada é o analisador automatizado de software. Estes analisadores são criados com base em boas práticas de codificação e padrões de projetos, sendo possível criar regras personalizadas de acordo com a realidade de cada projeto. Os analisadores de códigos ajudam a encontrar trechos que podem ser melhorados, ou seja, os trechos que estejam fora do padrão de codificação definido pela equipe, os que não estejam seguindo as boas práticas de programação, os códigos duplicados, os códigos com alta complexidade e baixo desempenho e segurança. Algumas ferramentas famosas na plataforma .NET utilizadas para análise estática de código são a FxCop e o StyleCop. São ferramentas gratuitas da Microsoft nas quais para cada uma dessas podem ser configuradas arquivos contendo as regras para avaliação do projeto. A partir deste arquivo que contém as regras de configuração é que cada ferramenta poderá
  10. 10. 10 avaliar o projeto com intuito de descobrir se alguma violação ocorreu, podendo gerar um arquivo legível para o servidor de Integração Contínua. Assim como existem as ferramentas de analisadores de códigos, várias outras estão disponíveis no servidor de integração contínua. Com foco na plataforma .NET temos o MSBuild, que compila aplicações em .NET; o MSTest, que executa testes automatizados; o NuGet, semelhante ao Maven na plataforma JAVA, que consegue resolver as dependências do projeto; e a possibilidade de executar linhas de comandos para o sistema operacional além de vários outros plugins e recursos fornecidos. Mesmo com várias etapas automatizadas sendo realizadas pelo servidor de integração contínua, a peça mais importante não deixa de ser a equipe de desenvolvimento. Shore (2005), “I A F ” importância da comunicação entre os membros da equipe ao detectar problemas como testes falhando ou versão commitada quebrando o build, que devem ser informados aos demais membros para que estes não realizem integração até que o problema seja elucidado. Existem disponíveis inúmeras ferramentas de Integração Contínua com as mais diversas possibilidades e limitações, sendo elas gratuitas ou pagas. A escolha da melhor ferramenta deverá tomar como principais critérios as necessidades e características de cada projeto. A tabela 1 encontra-se um comparativo entre as principais ferramentas utilizadas em ambiente .NET. Tabela 1 – Comparação de servidores de Integração Contínua Atributo Team Foundation Service Team City Bamboo Jenkins Origem Microsoft Jet Brains Atlassian Hudson - java.net Open Source Não Não Não, mas o código é fornecido aos clientes que comprarem Sim Issue Tracker Proprietário YouTrack - proprietário JIRA GitHub Desenvolvedores Ativos - 12 4+ 5-10 no core Localizado PT-BR Inglês Inglês Português Português Suporte a Git Sim Sim Sim Sim Suporte a SVN Não Sim Sim Sim Suporte a Mercurial Não Sim Sim Sim
  11. 11. 11 Suporte a TFS Sim Sim Não Sim Suporte a Perforce Não Sim Sim Sim Builds Simultâneos Sim Sim Sim Sim Builds Distribuídos Sim Sim Sim Sim Escaneamento de Dependências, Análise Estática, Relatórios Sim Sim (Resharper) Sim Sim (plugins) Builds Disparados por Alteração no CVS Sim Sim Sim Sim Notificação de Falha Sim Sim Sim Sim (plugin) Histórico de Builds Sim Sim Não Sim Cobertura de Código Sim Sim Sim Sim Autenticação de Usuário Sim Sim Sim Sim Schemas de Autorização de Usuário Sim Sim Sim Sim Email Sim Sim Sim Sim RSS Sim Sim Sim Sim Bandeja do Sistema – Windows Não Sim Sim (plugin) Sim (plugin) Log Sim Sim Sim Sim View de Mudanças Sim Sim Sim Sim Permite Adicionar Projetos Sim Sim Sim Sim Permite Clonar Projetos Não Sim Sim Sim Permite Deletar Projetos Sim Sim Sim Sim Permite Modificar Projetos Sim Sim Sim Sim Interromper o Build Sim Sim Sim Sim Pausar Builds Não Sim Não Não Acessar Aterfatos Gerados no Build Sim Sim Sim Busca por Builds Sim Sim Não Não Gráficos de Histórico Sim Sim Sim Sim Auto-Atualização das Páginas Sim Sim Sim Sim View de Vários Projetos Sim Sim Sim Sim MSBuild Sim Sim Sim Sim (plugin) NAnt Não Sim Sim Sim (plugin) Visual Studio Sim Sim Sim Não Bugzilla Não Sim Não Sim JIRA Não Sim Sim Sim (plugin) Próprio Sim Sim (YouTrack - Importa do Redmine) Sim (JIRA) Não Redmine Não Sim Não Sim (plugin) NUnit Não Sim Sim Sim (plugin) MSTest Sim Sim Sim Sim (plugin) Integração Visual Studio Sim Sim Sim (JIRA) Não Instalador para Windows Não Sim Sim Sim Dependências Adicionais Não JRE, plugins de CV JRE JRE Plataforma de Execução Nuvem JVM JVM JVM
  12. 12. 12 (Windows Azure) Plataforma do Projeto .NET Variada Variada Variada Necessário Modificar Scripts de Build Não Não Não Não Configuração XML XML XML XML Fonte: Próprio autor 4 ESTUDO DE CASO 4.1 CONTEXTO DO PROJETO A experiência analisada nesta monografia será realizada em uma empresa de desenvolvimento de software que tem foco na área da saúde. Atuante no mercado brasileiro há 15 anos, a mesma possui sua matriz localizada em Fortaleza, mas atende em todo o Brasil e atualmente está prospectando novos clientes no mercado americano. A empresa possui como principal produto um Sistema de Informação Laboratorial mais conhecido como LIS (Laboratory Information System), o qual foi desenvolvido, em sua primeira versão, na plataforma Delphi. Este produto tem como função apoiar os profissionais no gerenciamento da rotina de um laboratório de análises clínicas, onde se propõe a suprir todas as etapas do processo laboratorial, tais como: recepção do cliente (cadastro e coleta), área técnica (análise da amostra) e disponibilização do resultado para o cliente. Visando atender e superar as demandas do mercado, foi iniciado o desenvolvimento de um produto totalmente reformulado utilizando a plataforma .NET, na qual é desenvolvido sob exigências técnicas de padrões de segurança, qualidade e performance, onde possui módulos desktop (tecnologia Windows Forms) e web (tecnologia ASP MVC 3). É seguido, como orientação da codificação do projeto, o conjunto de práticas do XP na qual o uso do TDD e da programação em par é mais evidenciado na rotina de desenvolvimento. Para realizar o gerenciamento do projeto optou-se pela metodologia Scrum. Seguindo as recomendações do guia de Schwaber (2013) o sprint foi definido em um período de duas semanas, onde no final de cada sprint é realizada uma entrega para o Product Owner. Logo em seguida, é realizada a reunião de retrospectiva do sprint, com intuito de que todos os
  13. 13. 13 membros da equipe possam avaliar o sprint passado, bem como identificar as possíveis melhorias dos processos. 4.2 MOTIVAÇÃO PARA O USO DE TDD E INTEGRAÇÃO CONTÍNUA A equipe, desde o início do projeto, optou por utilizar o TDD, tendo avaliado as vantagens teóricas de se utilizar tal técnica de desenvolvimento e os inúmeros casos de sucesso percebidos em revistas, artigos, livros e materiais digitais. Boa parte da equipe não dominava a tecnologia da plataforma .NET, porém, os estudos e principalmente o uso do TDD, fez com que cada pedaço das funcionalidades pudesse ser pensado de forma simples, conseguindo, assim, resoluções de grande complexidade em pequenas etapas, o que fez com que o aprendizado fosse de mais fácil absorção. Conforme o projeto foi evoluindo, foi possível notar o alto grau de desacoplamento entre os objetos. Este desacoplamento foi crescendo em granularidade até que foi percebido que várias classes poderiam se tornar um componente reutilizado por outros projetos. Em determinados pontos estes projetos poderiam ser exportados para soluções diferentes, visto que no ambiente .NET é natural se agrupar vários projetos em uma Solution (solução). O agrupamento por Solution tem como objetivo deixar o ambiente o mais coeso possível, evitando que projetos que não façam parte de um mesmo escopo integrem uma mesma Solution. A característica de baixo acoplamento e alta coesão é dita como uma das vantagens que o TDD proporciona ao ser utilizado. Esta característica proporcionou à suíte a vantagem de reaproveitar componentes comuns em inúmeros projetos. Com os benefícios de ter vários componentes, surgiu a necessidade de utilizá-los de forma simples, assim como de realizar a atualização dos componentes quando os mesmos forem disponibilizados. Fazer isto de forma manual, ou seja, configurando diretamente os componentes nas referências do projeto é algo que, conforme o projeto ia evoluindo, passou a se tornar inviável. Então, visto o trabalho que era gerado a cada atualização de componente, que fazia com que fosse necessário que os demais projetos fossem atualizados manualmente, optou-se pela utilização do NuGet. O NuGet é um gerenciador de pacotes que além de garantir a
  14. 14. 14 facilidade de atualizar os componentes com segurança, fornece uma maior agilidade fazendo com que toda a Solution possa ser atualizada de uma única vez. A figura 4 ilustra um exemplo de uso do NuGet na qual o projeto Autenticação, que é responsável por fazer o controle de aceso de usuários, é publicado no repositório NuGet, ficando disponível para os demais projetos da suíte. Os projetos Atendimento e Resultado o instalam a partir do repositório, sem a necessidade de referenciar diretamente a dll do projeto Autenticação. Figura 4 – Visão gera do uso do NuGet Fonte: Próprio autor Caso sejam realizadas modificações no módulo de Autenticação, para que os demais módulos que o utilizam percebam esta alteração, será preciso ir em cada um dos módulos e solicitar ao NuGet que realize a atualização de seus componentes. Fazer este passo para apenas dois módulos não demanda um tempo considerável, porém, conforme a suíte foi evoluindo, a quantidade de projetos que reutilizavam outros componentes foi aumentando quase exponencialmente. Com o crescimento natural da suíte, o reaproveitamento de componentes foi ocorrendo com maior frequência até que conseguimos cenários como, por exemplo, o projeto Atendimento depender do projeto Autenticação que por sua vez depende do projeto Logger, chegando a cenários onde haveria até seis níveis de dependências, construindo uma árvore de dependências cada vez maior. Em cenários onde é preciso implementar uma nova funcionalidade no projeto localizado na folha da árvore de dependências, como por exemplo o projeto Logger, é necessário que todos os nós intermediários da árvore sejam atualizados, para que o projeto
  15. 15. 15 localizado na raiz, como por exemplo o projeto Atendimento, consiga perceber as alterações. A figura 5 exemplifica melhor a situação supracitada. Figura 5 – Árvore de dependências Fonte: Próprio autor Devido à estratégia de uso de componentes é natural o uso de um componente por vários outros, visto que o reaproveitamento é algo que agrega bastante valor à suíte, evitando repetição de código e facilitando a sua manutenção. Um exemplo disto é o componente Logger que é utilizado tanto pelo projeto Autenticação como pelo projeto Integração Caixa. Para cada componente a ser publicado, deve-se atualizar todos os componentes com os quais ele possua dependência, executar os testes automatizados e, caso os testes passem, publicar o componente no repositório NuGet. Este processo deverá ser feito para cada nó, no sentido da folha até a raiz. Este processo de atualização de componente a componente é algo que, além de demandar tempo, é um processo rotineiro e bastante propício a erros humanos como, por exemplo, uma dependência que deixa de ser atualizada e ao ser publicada no repositório faz com que os projetos que a utilizam continuem apontando para uma versão antiga, fazendo com que o desenvolvedor tenha que percorrer toda a árvore de dependências, repetindo todo o processo.
  16. 16. 16 Com o sistema entrando em produção em seu primeiro cliente, os defeitos que passaram despercebidos pelos testes automatizados e manuais foram aparecendo, logo, a necessidade de disponibilização de versões com correções o mais rápido possível foi se tornando prioritária. Percebeu-se, portanto, que seria necessário encontrar uma forma de automatizar o processo de integração da suíte. A partir da experiência profissional e acadêmica dos membros da equipe foi levantada a opção de uso de um sistema de integração contínua. Este deveria ser capaz de verificar se toda a suíte encontra-se estável para então ser possível realizar a geração de versões com todos os componentes atualizados. 4.3 CONFIGURAÇÃO DO SERVIDOR DE IC Foram realizadas pesquisas de sistemas de integração contínua e, diante do resultado das pesquisas, optou-se pelo uso do Jenkins devido a grande quantidade de material de estudo, plugins, comunidade ativa e por ser multiplataforma. Para realizar a compilação dos projetos optou-se por utilizar o MSBuild devido ser parte integrante do .NET Framework. Os testes automatizados foram desenvolvidos utilizando a framework MSTest e, a execução destes, foi realizada via linha de comando usando o próprio MSTest. O servidor disponibilizado para configuração da integração contínua possui o sistema operacional Windows Server 2008, processador Xnon, 8 MB de memória e 250GB de espaço de HD. Após realizada a instalação do servidor Jenkins, foram instalados os seguintes plugins:  MSBuild Plugin: responsável por dar suporte ao build aos projetos .NET;  MSTest Runner: permite executar os testes via linha de comando;  MSTest Plugin: permite tratar o arquivo de resultado de execução dos testes para ser compreendido pelo Jenkins;  Subversion Plugin: dá suporte ao Jenkins a ter acesso ao controle de versões SVN. O Jenkins trabalha com uma unidade conhecida por Job. Cada Job pode ser configurado de acordo com a realidade de cada projeto. Neste estudo de caso foi utilizada a lógica de um Job para cada componente. Para iniciar as configurações dos Jobs foi criado um teste com intuito de validar o funcionamento do Jenkins instalado no servidor.
  17. 17. 17 Assim, o projeto mais simples levantado, que possui um cenário mínimo de testes automatizados, foi o projeto Logger. O Jenkins fornece, por padrão, uma lista de possibilidades e configurações que são aprimoradas pelos plugins. Dentro da sequência natural para configurar o Job do Logger, foram realizados os seguintes passos: 1. baixar o código fonte do controle de versão; 2. instalar os pacotes que o projeto possui dependência via Nuget; > nuget restore 3. verificar atualizações de componentes que ele utiliza via Nuget; > nuget update packages.config 4. realizar build no projeto via MSBuild; > MSBuild.exe /t:Clean;Build /p:Configuration=Release Logger.sln 5. executar os testes automatizados com MSTest; > MSTest /testcontainer:..TestLogger.dll /testcontainer:..TestLoggerBanco.dll /runconfig:Local.testsettings /resultsfile:TestResultsTestLoggerResults.trx 6. publicar o pacote do projeto Logger; > Nuget pack Logger.csproj Com estes passos foi possível validar o ambiente do servidor para realizações da rotina mínima necessária para o funcionamento da Integração Contínua, partindo então para a realização da configuração para todos os componentes. Para que fosse obedecido o fluxo de execução correta dos Jobs, foi preciso realizar um levantamento em cada projeto para avaliar as dependências que cada um possuía. Assim, foram baixados para a máquina de desenvolvimento todos os fontes de todos os componentes existentes na suíte. A partir da experiência da equipe foi feita a análise seguindo a ordem dos projetos mais simples, com pouca ou nenhuma dependência, para os mais complexos. Esta análise foi realizada a partir da abertura o projeto no Visual Studio, avaliando as dependências existentes via NuGet e então, registrada em uma ferramenta gráfica. Conforme era constatado que o projeto não possuía dependência ou que tivesse todas suas dependências já configuradas no Jenkins, então esta seria configurada e também registrada na árvore de dependências.
  18. 18. 18 O gráfico de dependências, conforme foi sendo feito o levantamento, foi ficando bastante poluído visualmente e confuso, devido à enorme quantidade de projetos e o alto grau de reaproveitamento. Encontra-se na figura 6 um exemplo de como o grafo estava evoluindo. Figura 6 – Árvore de dependências de parte da suíte Fonte: Próprio autor O levantamento da árvore de dependências era uma tarefa operacional e bastante extensiva onde, de acordo com a evolução do projeto, manter este arquivo de gráfico das dependências passaria a ficar bastante burocrático e suscetível a erros humanos. Com esta preocupação em mente, foi pensando em uma forma de contornar esta necessidade de levantamento manual de projeto a projeto por um analista. Então, foi desenvolvido um aplicativo que seria capaz de scanear as pastas dos repositórios e traçar as dependências existentes entre todos os projetos. A exibição do resultado da análise de dependências seria em forma de árvore, em que é possível expandir até chegar o nível mais baixo, conforme apresenta a figura 7. Figura 7 – Árvore de projetos e suas dependências Fonte: Próprio autor
  19. 19. 19 Ao selecionar qualquer um dos projetos, é exibida no mesmo aplicativo uma relação dos programas que o utilizam, ou seja, é possível visualizar quais as dependências de cada projeto e também quais os seus dependentes, conforme figura 8. Figura 8 – Árvore de projetos e seus dependentes Fonte: Próprio autor Com a automação dessa rotina, foi possível, além de garantir a fidelidade dos dados de dependências exibidos dos projetos, perceber algumas falhas em certos projetos como dependência circular e projetos com dependências desnecessárias. Com isso, o aplicativo se tornou uma ferramenta que proporcionou de imediato uma melhor visão dos componentes da suíte. A partir do uso desta ferramenta, foi possível acelerar a etapa de configuração e também a definição de um fluxograma de execução dos Jobs. A seguir, a figura 9 exemplifica a execução do processo de integração contínua de uma forma geral.
  20. 20. 20 Figura 9 – Fluxo de execução da integração contínua Fonte: Próprio autor Quando ocorre a quebra de algum Job, por qualquer motivo, é enviado um email para a equipe de desenvolvimento informando o ocorrido, fazendo com que a mesma possa intervir e permitir que toda a suíte permaneça integrada ao máximo. A equipe optou por deixar a integração contínua sempre em execução para que, qualquer alteração que seja feita em um determinado componente, caso este cause a quebra de outro projeto, possa ser percebido pela equipe o mais rápido possível. Esse comportamento auxilia a equipe, na medida em que não é necessário que o desenvolvedor realize a atualização em todos os projetos em que um determinado projeto é dependido, para poder perceber se há alguma incompatibilidade ou bug gerado após determinadas atualizações. Após concluir a configuração de todos os componentes existentes na suíte, foi possível medir o tempo total para executar toda a integração da suíte. Dos projetos folhas até os projetos raízes foi contabilizado um tempo total de 75min. Com intuito de diminuir este tempo total de execução foi paralelizada a execução de alguns Jobs que não possuíam dependências entre si. Com isso, foi alcançada uma otimização de 20% do tempo total.
  21. 21. 21 Essa economia de tempo tornou possível ter uma versão estável a cada 1h, o que, no processo manual de atualizações de componentes, ou seja, sem integração contínua, poderia levar até 8h de trabalho. Com a integração contínua em funcionamento, os membros da equipe passaram a respeitar o fluxo de trabalho junto ao controle de versão, visto que, a integração é executada no trunk de desenvolvimento, e este deveria permanecer estável o máximo possível. Logo, sempre era criado um novo branch para cada funcionalidade e esta, quando concluída, era reintegrada ao trunk. Em casos onde, após uma reintegração de um branch ao trunk, com a quebra de algum projeto acusado pela Integração Contínua, o desenvolvedor, ou o par de desenvolvedores, que estava desenvolvendo, fica responsável por analisar o motivo que o fez quebrar, corrigir e reiniciar a integração a partir do projeto que quebrou. Com esta forma de organização, toda a equipe assume a responsabilidade de manter a suíte sempre atualizada e sincronizada, sendo uma forma justa para balancear o trabalho de manter a integração contínua sempre funcionando. 4.4 BENEFÍCIOS Logo após a conclusão das configurações do servidor de integração contínua, foi percebida, quase que de imediato, uma melhoria na rotina de desenvolvimento da equipe. A infraestrutura do ambiente de desenvolvimento se tornou mais confiável, o que fez com que o projeto pudesse continuar crescendo de forma rápida e segura. Com todos os projetos utilizando TDD e configurados no servidor de integração contínua, os membros da equipe ganharam mais confiança para realizar refatorações com maior frequência, visto que os projetos estariam sempre sendo avaliados pelo servidor e isto proporcionou a toda suíte um código cada vez mais fácil de se dar manutenção. O aumento na velocidade de desenvolvimento de novas funcionalidades foi perceptível, visto que os membros passaram a concentrar seus esforços apenas no desenvolvimento de componentes, não precisando se preocupar em atualizar os demais projetos e, ainda, assegurar que os projetos atualizados continuariam funcionando adequadamente. Houve também ganho que não se enquadra apenas em aspectos técnicos, como a facilidade de se integrar novos membros à equipe de desenvolvimento, visto que todos os
  22. 22. 22 testes procuram assegurar os comportamentos necessários que a aplicação precisa, permitindo que novos membros consigam programar em um ambiente guiado por testes, minimizando a inclusão de bugs ou comportamentos indesejados. A identificação de bugs antes mesmo da versão chegar ao cliente mostrou ser o maior ganho dentre os demais, visto o histórico recente de versões com bugs instaladas em clientes e que só eram percebidos durante o uso em produção pelo cliente final. Isto gerava um desgaste desnecessário da empresa junto ao cliente e um esforço enorme da equipe para resolver o problema em um ambiente de produção. 4.5 PRÓXIMOS PASSOS Com a garantia dos testes automatizados e da integração contínua, é vislumbrado o próximo passo, que seria a geração dos instaladores dos módulos e sua disponibilização no servidor de versões. Estas versões seriam validadas pelo setor de suporte e implantação para então serem disponibilizadas para os clientes. Será estudado sobre Continuous Delivery (CD), para que seja de conhecimento da equipe quais práticas estão sendo adotadas por demais empresas no mercado quanto ao processo de disponibilização de novas versões de seus aplicativos. Conforme forem os resultados dos estudos, será conversado entre os membros da equipe para que seja definido como funcionará o processo e a estratégia para implementação no servidor de integração contínua. Também é desejado diminuir o tempo total de execução para realizar a integração de toda a suíte. Para isto estão sendo considerados dois itens que podem ser melhorados. O primeiro seria permitir que os testes de integração com o banco de dados sejam feitos em memória utilizando o banco de dados SQLite. O segundo ponto seria conseguir paralelizar mais a execução de mais Jobs na integração contínua, sejam elas em outras máquinas como cluster ou no próprio servidor de integração. O objetivo seria ter um ganho de 50% no tempo total de execução. 5 CONCLUSÃO Foram apresentados neste trabalho os benefícios de se utilizar, em um projeto de desenvolvimento de software, TDD junto com um servidor de integração contínua.
  23. 23. 23 Para que fosse possível alcançar o objetivo deste trabalho, foi preciso realizar pesquisas para escolher quais ferramentas seriam utilizadas no servidor de IC. A partir desta definição, foi realizada a configuração do servidor de IC em um projeto em que a equipe de desenvolvimento já seguia as orientações do TDD. Houve dificuldades no decorrer das configurações dos projetos da suíte na integração contínua por algumas limitações da plataforma .NET e do sistema operacional Windows. Uma limitação percebida foi que alguns projetos necessitavam de testes de tela e estes não puderam ser executados pelo servidor de IC, visto que este é um serviço do Windows, e há uma limitação dos serviços do Windows, que não permitem interação com o usuário. Com isso, alguns projetos ficaram sem a etapa de execução dos testes automatizados. Sobre esta limitação dos testes de tela, foram buscadas soluções. Foi encontrada uma solução na plataforma JAVA que simula um ambiente de interação com usuário sem a necessidade de se ter um ambiente real, o que resolveria a limitação dos serviços Windows ao rodar testes de tela. No entanto, solução similar a esta não foi encontrado para plataforma .NET. Também foi percebida uma limitação do Windows quanto à quantidade máxima de arquivos temporários. Ao ser executado o fluxo de integração, são criados arquivos temporários no Windows pelo Jenkins e estes não são apagados automaticamente pelo sistema operacional, então, quando é atingido o tamanho máximo de arquivos no diretório o fluxo é parado, sendo preciso que seja realizada uma rotina para limpar a pasta temporária e reabilitar o fluxo de integração. A equipe, desde o início das configurações dos projetos no servidor de IC, sempre apoiou a iniciativa. No entanto, no decorrer do desenvolvimento, houve situações em que alguns membros esqueciam da existência do servidor e deixavam projetos com build quebrado e a integração parada. Este tipo de problema foi rapidamente contornado com uma melhor definição do processo. A utilização do TDD foi algo que desde o início do projeto foi bem aceito pela grande maioria da equipe, mesmo visto que alguns membros da equipe precisaram passar por uma fase de aprendizagem e aceitação desta técnica. Após um certo tempo de andamento do projeto, foi percebida pela equipe a necessidade de se garantir os inúmeros componentes integrados constantemente. Esta surgiu a partir do momento que atualizar os componentes de forma manual consumia bastante tempo dos membros das equipes.
  24. 24. 24 Por fim, com o desenvolvimento deste trabalho em um ambiente real de desenvolvimento de aplicativos, foi possível perceber várias melhorias, tais como: aumento da qualidade de código, maior quantidade de código reaproveitável, melhor aproveitamento do tempo, garantia da suíte constantemente testada e integrada, feedback constante para a equipe desenvolvimento, aumento de segurança para realizar mudanças no projeto. A partir deste trabalho, é possível que novos estudos sejam realizados, tais como:  comparação da implementação de integração contínua em servidor na nuvem ou local;  utilização de servidor dedicado para realizar execução de testes de tela;  otimização do tempo de execução da integração contínua utilizando clusters;  configurar um ambiente multiplataforma de Integração Contínua;  aplicação de Continuous Delivery em um ambiente garantido por Integração Contínua;  análise da velocidade de desenvolvimento com TDD e sem TDD;  análise do esforço de refatoração de código em projetos com e sem TDD. REFERÊNCIAS BECK, Kent. Extreme Programming Explained: Embrace Change. 1.ed. Addison-Wesley, 1999. . Test-Driven Development: By Example. 1.ed. Addison-Wesley, 2002. BOEKEL, Rafael Van. TDD na prática. Revista Java Magazine, v. 89, 2010. FEATHERS, Michael. Working Effectively With Legacy Code. 2002. Disponível em: < http://www.objectmentor.com/resources/articles/WorkingEffectivelyWithLegacyCode.pdf>. Acesso em: 04 Mai. 2013. FOWLER, Martin. Continuous Integration. 2009. Disponível em: <http://martinfowler.com/articles/continuousIntegration.html>. Acesso em: 02 Mai. 2013. Google Group, Como convencer a equipe à usar TDD?. Disponível em: <https://groups.google.com/forum/#!topic/tdd-no-mundo-real/N36F_NaTM5A>. Acesso em: 15 Out. 2013. GARETH. T  ? 2012. Disponível em: <http://www.redhills.ie/2012/08/08/should-a-startup-use-tdd/>. Acesso em: 03 Mai. 2013.
  25. 25. 25 HAGEMANN, Stephan. The local optimum of Test-Driven Development. 2011. Disponível em: <http://devblog.xing.com/ruby-on-rails/the-local-optimum-of-test-driven-development/>. Acesso em: 04 Mai. 2013. KAWALEROWICZ, Marcin; BERNTSON, Craig. Continuous Integration in .NET. Stamford: Manning, 2011. KOSKELA, Lasse. Test Driven: Practical TDD and Acceptance TDD for Java Developers. Stamford: Manning, 2007. MARK BERG, Alan. Jenkins Continuous Integration Cookbook. Birmingham: Packt Publishing Ltd , 2012. MOREIRA, Anderson. Integração Contínua. Disponível em: <http://siep.ifpe.edu.br/anderson/blog/?page_id=1015>. Acesso em: 05 Mai. 2013. SCHWABER, Ken; SUTHERLAND, Jeff. Guia do SCRUM. 2013. Disponível em: <https://www.scrum.org/>. Acesso em: 10 Out. 2013. SHORE, James. Continuous Integration is an Attitude, Not a Tool. p. 1, 2005. Disponível em: <http://www.jamesshore.com/Blog/Continuous-Integration-is-an-Attitude.html>. Acesso em: 03 Mai. 2013. . The Art of Agile Development: Test-Driven Development. p. 1–14, 2010. Disponível em: <http://www.jamesshore.com/Agile-Book/test_driven_development.html>. Acesso em: 03 Mai. 2013. STEPHENS, Matt; ROSENBERG, Doug. Design Driven Testing: Test Smarter, Not Harder. New York: Apress, 2010. WHITEHEAD, Nicholas. Continuous integration with Hudson. JavaWorld.com, 2012. Disponível em: <http://www.javaworld.com/javaworld/jw-12-2008/jw-12-hudson-ci.html>. Acesso em: 05 Mai. 2013.

×