Witam w drugiej części poradnika dotyczącego użycia TeamCity w projektach php. W tym odcinku zajmę się integracją z phpunit.

Integracja

Instalacja phpunit jest banalnie prosta: wystarczy wydać następujące polecenia na maszynie gdzie będą uruchamiane testy jednostkowe:

pear channel-discover pear.phpunit.de
pear channel-discover components.ez.no
pear channel-discover pear.symfony-project.com
pear install phpunit/PHPUnit

I zostaną zainstalowane wszystkie niezbędne pakiety.

Teraz wystarczy przygotować plik konfiguracyjny w formacie xml. Ja używam następującej konfiguracji:

<?xml version="1.0" encoding="UTF-8" ?>
<phpunit backupGlobals="false"
        backupStaticAttributes="true"
        bootstrap="bootstrap.php"
        colors="false"
        convertErrorsToExceptions="true"
        convertNoticesToExceptions="true"
        convertWarningsToExceptions="true"
        processIsolation="false"
        stopOnFailure="true"
        syntaxCheck="false"
        verbose="true"
        strict="true">

        <testsuite name="Application">
                <directory>./</directory>
        </testsuite>

        <logging>
                <log type="coverage-clover" target="tests/artifacts/phpunit/coverage.xml" />
                <log type="junit" target="tests/artifacts/phpunit/phpunit.xml" logIncompleteSkipped="true" />
                <log type="testdox-html" target="./logs/testdox.html" />
        </logging>
</phpunit>

Osoby używające wcześniej phpunit mogą być zdziwione brakiem generowania raportu typu html ale powód jest bardzo prosty: jego generacja trwa za długo, zajmuje zbyt wiele pamięci i jego generowanie kilka razy dziennie jest całkowicie zbędne. Generowanie tylko raportu XML jest szybsze. Tworzę dodatkową konfigurację która dodaje komendę linii poleceń do generowania raportu HTML raz w tygodniu. Raport XML jest potrzebny do śledzenia pokrycia kodu testami w postaci wykresu – ale o tym za chwilę, w części bonusowej, jeżeli nie chcesz śledzić tych informacji możesz usunąć coverage-clover z procesu.

Teraz zmodyfikujemy utworzony poprzednio plik build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="Application" default="checkout">

    <property name="artifactsDir" value="${basedir}/tests/artifacts" description="Base dir for all output of build commands" />

    <target name="prepare">
        <delete includeemptydirs="true" description="Clear artifacts dir">
            <fileset dir="${artifactsDir}" includes="**/*" />
        </delete>

        <mkdir dir="${artifactsDir}/" />
    </target>

    <target name="checkout" description="Base task - update directory with new version from server and overwrite all conflicts with files from server">
        <exec executable="svn" failonerror="true" output="${artifactsDir}/svn.log">
            <arg line="up" />
            <arg line="--force" />
            <arg line="--accept theirs-full" />
        </exec>
    </target>

    <target name="phpunit" depends="prepare">
        <mkdir dir="${artifactsDir}/phpunit/" description="Make sure that artifacts directory exists" />

        <exec dir="${basedir}" executable="phpunit" failonerror="true" output="${artifactsDir}/phpunit.log" >
            <arg line="--configuration ${basedir}/tests/phpunit.xml" />
        </exec>
    </target>

    <target name="phpunit-html-report" depends="prepare">
        <mkdir dir="${artifactsDir}/phpunit/" description="Make sure that artifacts directory exists" />

        <exec dir="${basedir}" executable="phpunit" failonerror="true" output="${artifactsDir}/phpunit.log">
            <arg line="--configuration ${basedir}/tests/phpunit.xml" />
            <arg line="--coverage-html ${artifactsDir}/phpunit/report" />
        </exec>

        <zip destfile="${artifactsDir}/coverage.zip" basedir="${artifactsDir}/phpunit/report" whenempty="skip" />
    </target>
</project>

Ok. Mamy więc integracje z phpunit, generują się raporty, jest specjalna akcja do generowania pokrycia w formie raportu html. Teraz trzeba to wszystko zaimportować do TeamCity.

Import danych (wiadomości serwisowe)

Podczas procesu integracji istnieje możliwość przekazania dodatkowych informacji do serwera. Można to zrobić albo modyfikując plik team-city.xml albo poprzez wyświetlanie z poziomu skryptu wiadomości serwisowych. Ten drugi sposób wydaje mi się prostszy a zarazem bardziej elastyczny więc na nim się skupię.

Wiadomość serwisowa ma następującą postać

##teamcity[message 'value']
##teamcity[message name1='value1' name2='value2']

Pierwsza to wiadomość o jednym argumencie a druga to wiadomość wielu argumentach. Dodatkowo wszystkie wiadomości o wielu argumentach obsługują dwa wspólne argumenty: timestamp oraz flowId. Pierwszy przyjmuje wartość czasową i służy dokładnemu raportowaniu a drugi używany jest w przypadku gdy proces integracji obejmuje wiele skryptów/zadań uruchomionych jednocześnie. Przykładowe wiadomości jakie można wysłać to:

##teamcity[buildStatus status='' text='{build.status.text} and some aftertext']
##teamcity[message text='' errorDetails='' status='']
##teamcity[testSuiteStarted name='suite.name']
##teamcity[testSuiteFinished name='suite.name']
##teamcity[testStarted name='testname']
##teamcity[testFinished name='testname' duration='50']

I najbardziej przydatne w dzisiejszym opisie:

##teamcity[publishArtifacts '']
##teamcity[importData type='junit' path='' whenNoDataPublished='error']

Celem tak złożonego procesu integracji jest możliwość łatwej zmiany maszyny na której przeprowadzany jest build. By to osiągnąć serwer musi wiedzieć które pliki zawierają raporty. Wiadomości te mogą być przekazane albo z poziomu skryptów PHP albo przy pomocy dyrektywy &lt;echo /&gt; skryptów ant. Zmodyfikowany plik build.xml wygląda następująco:

<?xml version="1.0" encoding="UTF-8"?>
<project name="Application" default="checkout">
    <property name="artifactsDir" value="${basedir}/tests/artifacts" description="Base dir for all output of build commands"/>
    
    <target name="prepare">
        <delete includeemptydirs="true" description="Clear artifacts dir">
            <fileset dir="${artifactsDir}" includes="**/*"/>
        </delete>
        <mkdir dir="${artifactsDir}/"/>
    </target>
    
    <target name="checkout" description="Base task - update directory with new version from server and overwrite all conflicts with files from server">
        <exec executable="svn" failonerror="true" output="${artifactsDir}/svn.log">
            <arg line="up"/>
            <arg line="--force"/>
            <arg line="--accept theirs-full"/>
        </exec>
        <echo message="##teamcity[publishArtifacts '${artifactsDir}/svn.log']" description="Import detailed svn log to TeamCity"/>
    </target>
    
    <target name="phpunit" depends="prepare">
        <mkdir dir="${artifactsDir}/phpunit/" description="Make sure that artifacts directory exists"/>
        <exec dir="${basedir}" executable="phpunit" failonerror="true" output="${artifactsDir}/phpunit.log">
            <arg line="--configuration ${basedir}/tests/phpunit.xml"/>
        </exec>
        <echo message="##teamcity[importData type='junit' path='${artifactsDir}/phpunit/phpunit.xml' whenNoDataPublished='error']" description="Import test status to TeamCity"/>
        <echo message="##teamcity[publishArtifacts '${artifactsDir}/phpunit.log']" description="Import phpunit log to TeamCity"/>
    </target>
    
    <target name="phpunit-html-report" depends="prepare">
        <mkdir dir="${artifactsDir}/phpunit/" description="Make sure that artifacts directory exists"/>
        <exec dir="${basedir}" executable="phpunit" failonerror="true" output="${artifactsDir}/phpunit.log">
            <arg line="--configuration ${basedir}/tests/phpunit.xml"/>
            <arg line="--coverage-html ${artifactsDir}/phpunit/report"/>
        </exec>
        <echo message="##teamcity[importData type='junit' path='${artifactsDir}/phpunit/phpunit.xml' whenNoDataPublished='error']" description="Import test status to TeamCity"/>
        <echo message="##teamcity[publishArtifacts '${artifactsDir}/phpunit.log']" description="Import phpunit log to TeamCity"/>
        <zip destfile="${artifactsDir}/coverage.zip" basedir="${artifactsDir}/phpunit/report" whenempty="skip"/>
        <echo message="##teamcity[publishArtifacts '${artifactsDir}/coverage.zip']"/>
    </target>
</project>

Informując TeamCity (a konkretnie agenta integrującego zainstalowanego na maszynie gdzie wykonuje się zadanie) o położeniu poszczególnych plików do importu możliwe jest wykonywanie integracji na dowolnej maszynie. Pliki raportów automatycznie zostaną przesłane z agenta na serwer do znanej lokalizacji. Plik phpunit.xml importowany pod koniec zostanie przetworzony przez serwer. Umożliwi to wyświetlanie ilości testów oraz śledzeniu statystyk projektu. I jeżeli chodzi o podstawową integrację to na tyle. Kolejny krok to extra bonus rozszerzający widok projektu o …

Dodatkowe statystyki

Wygenerowany wcześniej raport xml może zostać automatycznie przetworzony przez mały skrypt PHP który wyświetli najważniejsze statystyki projektu i umożliwi ich naniesienie na graf.

Prosty skrypt który parsuje raport w argumencie jest załączony do tego postu.

Teraz wystarczy zmodyfikować akcje phpunit o jeden wpis:

<code> <exec dir="${basedir}" executable="php" failonerror="false"> <arg line="-f ${basedir}/tests/clover_parser.php -- ${artifactsDir}/phpunit/coverage.xml" /> </exec></code>

Teraz w czasie integracji wyświetlony zostanie zestaw statystyk:

##teamcity[buildStatisticValue key='CodeCoverageAbsBCovered' value='245']
##teamcity[buildStatisticValue key='CodeCoverageAbsLCovered' value='209']
##teamcity[buildStatisticValue key='CodeCoverageAbsMCovered' value='36']
##teamcity[buildStatisticValue key='CodeCoverageAbsLTotal' value='979']
##teamcity[buildStatisticValue key='CodeCoverageAbsMTotal' value='85']
##teamcity[buildStatisticValue key='CodeCoverageAbsCTotal' value='8']

To wystarczy do wyświetlenia podstawowych statystyk w formie wykresów. By utworzyć własne wykresy na podstawie swoich danych wystarczy edytować plik &lt;TeamCity data dir&gt;/config/main-config.xml dla wykresów dostępnych we wszystkich projektach lub &lt;TeamCity data dir&gt;/config/&lt;Project Name&gt;/plugin-settings.xml dla poszczególnych projektów. <TeamCity data dir> to na ogół katalog .TeamCity w katalogu domowym użytkownika który uruchamia serwer teamcity. W plikach tych wystarczy dodać:

<graph title="Custom data" seriesTitle="Metric name"> <valueType key="key1" title="Metric 1" /> 
<valueType key="key2" title="Metric 2" /> 
<valueType key="BuildDuration" title="Duration" /> 
</graph> 

Zastępując oczywiście wartości elementami które chcemy przedstawić na grafie.

Attachments

  1. clover_parser (1.4 KiB)

26 lutego 2011 13:56 Grzegorz Drozd Brak komentarzy Komentuj Kategorie: Narz?dzia, PHP, Praca, TeamCity

Bądź uprzejma(y).

Możesz używać następujących tagów HTML tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Twój adres e-mail nie będzie wyświetlony.

Twój adres e-mail nie będzie przekazany nikomu.

Wszystkie komentarze są moderowane.