Concrete Logo
Hamburger button

(2+2) Integração Continua com .NET (fazendo na mão) – Parte 2 de 3

  • Blog
  • 5 de Março de 2013
Share

“The best way to find yourself is to lose yourself in the service of others.”

Mahatma Gandhi

Este artigo é a continuação de: (2+2) Integração Continua com .NET (fazendo na mão) – Parte 1 de 3

Na parte 3: configurando o seu Jenkins para rodar na máquina de DEV (1o ambiente integrado).

Depois de configurar o seu projeto no Visual Studio o Jenkins será capaz de fazer o mesmo e selecionar a configuração adequada para os testes locais.

Não vou abordar nesse artigo o script de bootstrap para você instalar e configurar o Jenkins de forma automática. Ainda assim, vale a pena explorar essa opção para todo o seu time.

Para baixar o Jenkins você pode usar algo como:

curl -oL $folder_lib/"jenkins.war" $url_jenkins

Para iniciar o Jenkins pela linha de comando use algo como:

$(java -jar $folder_lib/jenkins.war --httpListenAddress=$httpAddress --httpPort=$httpPort &) &

Com o Jenkins iniciado podemos utilizar o CLI para configurar o Jenkins.

Utilizando um script shell como o seguinte você poderá instalar de forma automática os plugins que vamos utilizar no job local do Jenkins e depois no job do ambiente integrado do Jenkins.


#montando lista de plugins
for plugin in mercurial msbuild nunit ncover
do
echo -e "\ninstalando plugin "$plugin", aguarde..."
java -jar $folder_lib/jenkins-cli.jar -s https://$httpAddress:$httpPort install-plugin $plugin -deploy
done

De forma manual, ou automática via bootstrap, instale os plugins:

mercurial – eu uso o mercurial no bitbucket, git fica como exercício para o leitor.

msbuild

nunit

ncover

Depois de configurar o Jenkins é necessário configurar o seu Job Local. Ele será muito útil para te avisar se você quebrou o build antes do ambiente integrado e de atrapalhar a vida do resto do time.

O arquivo de configuração do seu job Local ficará parecido com o seguinte:

[sourcecode]
<?xml version=’1.0′ encoding=’UTF-8′?>
<project>
<actions/>
<description></description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>Configuration</name>
<description></description>
<defaultValue>LOCAL_Voce</defaultValue>
</hudson.model.StringParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class=”hudson.plugins.mercurial.MercurialSCM” plugin=”mercurial@1.42″>
<installation>Mercurial</installation>
<source>C:\FolderDoProjeto\src</source>
<modules></modules>
<branch>botao</branch>
<clean>true</clean>
</scm>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers class=”vector”>
<hudson.triggers.SCMTrigger>
<spec>*/10 * * * *</spec>
</hudson.triggers.SCMTrigger>
</triggers>
<concurrentBuild>false</concurrentBuild>
<builders>
<hudson.plugins.msbuild.MsBuildBuilder plugin=”msbuild@1.16″>
<msBuildName>.NET 3.5</msBuildName>
<msBuildFile>MinhaSolution.sln</msBuildFile>
<cmdLineArgs></cmdLineArgs>
<buildVariablesAsProperties>true</buildVariablesAsProperties>
<continueOnBuildFailure>false</continueOnBuildFailure>
</hudson.plugins.msbuild.MsBuildBuilder>
<hudson.tasks.BatchFile>
<command>”%programfiles(x86)%\TestDriven.NET 3\NCover\1.5.8\NCover.Console.exe” //reg //w “%WORKSPACE%” //x “coverage-result.xml” “%programfiles(x86)%\TestDriven.NET 3\NUnit\2.6\nunit-console-x86.exe” /xml=nunit-result.xml /exclude:Funcional,Integracao /config:”%Configuration%” “MinhaSolution.sln”</command>
</hudson.tasks.BatchFile>
<hudson.tasks.BatchFile>
<command>”C:\FolderDoProjeto\bin\ReportGenerator_1.7.1.0\bin\ReportGenerator.exe” -reports:coverage-result.xml -targetdir:ncover -reporttypes:Html</command>
</hudson.tasks.BatchFile>
</builders>
<publishers>
<hudson.plugins.nunit.NUnitPublisher plugin=”nunit@0.14″>
<testResultsPattern>nunit-result.xml</testResultsPattern>
<debug>false</debug>
<keepJUnitReports>false</keepJUnitReports>
<skipJUnitArchiver>false</skipJUnitArchiver>
</hudson.plugins.nunit.NUnitPublisher>
<ncover.NCoverArchiver plugin=”ncover@0.3″>
<coverageDir>ncover</coverageDir>
<indexFileName>index.htm</indexFileName>
<keepAll>true</keepAll>
</ncover.NCoverArchiver>
<hudson.tasks.Mailer>
<recipients>seu-email@seu-dominio.com.br</recipients>
<dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild>
<sendToIndividuals>false</sendToIndividuals>
</hudson.tasks.Mailer>
</publishers>
<buildWrappers/>
</project>
[/sourcecode]

Você pode criar o job manualmente ou criar de forma automática através do CLI.

java -jar $folder_lib/jenkins-cli.jar -s https://$httpAddress:$httpPort create-job "Local Meu Projeto Job" < ArquivoJob.xml

Vamos olhar então cada parte do arquivo e o que mais precisa ser configurado para tudo funcionar.

O item do arquivo é a criação de um parâmetro para o job com um valor default para o seu ambiente.

[sourcecode]
<hudson.model.StringParameterDefinition>
<name>Configuration</name>
<description></description>
<defaultValue>LOCAL_Voce</defaultValue>
</hudson.model.StringParameterDefinition>
[/sourcecode]

Veja então na tela de configuração do job como as coisas ficam:

JenkinsParameter

Na sequência, configure o Jenkins para olhar o seu repositório local. O Jenkins rodará este job quando houver novos commits no repositório local.

Se o job local falhar, é sinal de que ao sincronizar com o repositório remoto seus colegas vão ficar chateados contigo…

[sourcecode]
<scm class=”hudson.plugins.mercurial.MercurialSCM” plugin=”mercurial@1.42″>
<installation>Mercurial</installation>
<source>C:\FolderDoProjeto\src</source>
<modules></modules>
<branch>nome_do_branch_se_houver</branch>
<clean>true</clean>
</scm>
[/sourcecode]

Veja na tela onde fazer isso:

MercurialLocal

Vou pular a parte de polling, fica como dever de casa conhecer cada parâmetro e como usar.

A próxima parte do config do job trata da configuração para o msbuild plugin.

[sourcecode]
<msBuildName>.NET 3.5</msBuildName>
<msBuildFile>MinhaSolution.sln</msBuildFile>
<cmdLineArgs></cmdLineArgs>
<buildVariablesAsProperties>true</buildVariablesAsProperties>
[/sourcecode]

É o suficiente para o msbuild gerar seus binários, incluindo a substituição dos arquivos de config, da mesma forma que o Visual Studio fez na 1a parte deste artigo.

MsBuild

Não esqueça de marcar a passagem de variáveis. É o parâmetro -Configuration:LOCAL_Voce que diz ao MsBuild.exe qual configuração usar.

O próximo passo é fundamental, onde efetivamente os testes são executados e os relatórios gerados.

[sourcecode]
<hudson.tasks.BatchFile>
<command>”%programfiles(x86)%\TestDriven.NET 3\NCover\1.5.8\NCover.Console.exe” //reg //w “%WORKSPACE%” //x “coverage-result.xml” “%programfiles(x86)%\TestDriven.NET 3\NUnit\2.6\nunit-console-x86.exe” /xml=nunit-result.xml /exclude:Funcional,Integracao /config:”%Configuration%” “MinhaSolution.sln”</command>
</hudson.tasks.BatchFile>
[/sourcecode]

Para rodar os testes utilizamos o NCover e o NUnit. Em particular, a versão que utilizei foi a empacotada no TestDriven.NET. Para mim, o valor do pacote profissional (hoje em $169) ou enterprise (iniciando em $230) vale.

Há também um versão gratuita para uso pessoal. Então você só precisa comprar se realmente for usar profissionalmente.

Os parâmetros acima com // são passados para o NCover, os parâmetros com / vão para o NUnit.

O NCover vai gerar um arquivo XML com sua cobertura de código. O parâmetro // reg faz com que o NCover registre sua dll a cada execução. Ainda assim você pode ter problemas com o NCover não conectando ao NUnit. ‘Profiler Connection Termination’

Se isso acontecer, execute o command como administrador. Vá até o diretório da dll e a registre novamente regsvr32.exe Coverlib.dll.

O parâmetro //x determina o nome do arquivo que o NCover vai gerar, nesse caso: coverage-result.xml. Esse arquivo será usado mais adiante para gerar o relatório de cobertura em html.

Para o NUnit, /xml=nunit-result.xml indica o arquivo xml do resultado dos testes unitários. O /exclude:Funcional,Integracao está excluindo (pelo menos por enquanto) teste funcionais e de integração. A parte 3 do artigo vai tratar da parte funcional específicamente.

Por enquanto, queremos rodar apenas os testes unitários no nosso ambiente local.

Por último, o parâmetro /config:”%Configuration%” diz ao NUnit a configuração que você está usando.

No exemplo utilizado, em vez do usual que é indicarmos o projeto de testes especificamente, estamos apontando para o Solution. Assim o NUnit vai rodar todos os testes da Solution.

Veja como fica a tela com o comando do NCover e NUnit.

NCover

O Report Generator transforma o xml gerado pelo NCover em formato html.

Se você chegou até aqui o trabalho pesado já está pronto. Faltam apenas as ações do post-build publicando tudo o que você fez.

PostBuild

NUnit publisher vai publicar o arquivo nunit-result.xml

NCover publisher vai publicar o html gerado pelo Report Generator. Dentro do diretório ncover o indice será, naturalmente, o arquivo index.htm.

Após a publicação, o que você vai ver na página do seu projeto é:

Resultado

Last, but not least… Não esqueça de configurar o seu email.

[sourcecode]
<hudson.tasks.Mailer>
<recipients>seu-email@seu-dominio.com.br</recipients>
<dontNotifyEveryUnstableBuild>false</dontNotifyEveryUnstableBuild>
<sendToIndividuals>false</sendToIndividuals>
</hudson.tasks.Mailer>
[/sourcecode]

Na parte 3 vamos olhar como fica no ambiente de DEV, incluindo os testes funcionais com Selenium.