W tym artykule poszerzymy plik Anta build.xml o testy jednostkowe przeprowadzane przy pomocy PHPUnit.
Przed uruchomienie testów jednostkowych powinniśmy sprawdzić czy kod PHP nie zawiera błędów składniowych. Kod poniżej implementuje cel, który używa zadania <apply>
Apache Ant do wywołania PHP lint checker (-l
) dla każdego pliku z kodem źródłowym w katalogach src
i tests
. Zadanie <apply>
działa tak jak <exec>
ale może działać na wielu plikach przekazanych jako <fileset>
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<target name="lint" unless="lint.done" description="Perform syntax check of sourcecode files"> <apply executable="php" taskname="lint"> <arg value="-l" /> <fileset dir="${basedir}/src"> <include name="**/*.php" /> <modified /> </fileset> <fileset dir="${basedir}/tests"> <include name="**/*.php" /> <modified /> </fileset> </apply> <property name="lint.done" value="true"/> </target> |
Do przeprowadzania testów jednostkowych wykorzystamy PHPUnit. Plik konfiguracyjny phpunit.xml.dist
wygląda następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="tests/bootstrap.php" backupGlobals="false" backupStaticAttributes="false" strict="true" verbose="true"> <testsuites> <testsuite name="ProjectName"> <directory suffix="Test.php">tests/unit/</directory> <directory suffix="Test.php">tests/integration/</directory> </testsuite> </testsuites> <logging> <log type="coverage-html" target="build/coverage"/> <log type="coverage-clover" target="build/logs/clover.xml"/> <log type="coverage-crap4j" target="build/logs/crap4j.xml"/> <log type="junit" target="build/logs/junit.xml" logIncompleteSkipped="false"/> </logging> <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> <exclude> <file>src/bootstrap.php</file> </exclude> </whitelist> </filter> </phpunit> |
Wywołanie phpunit bez argumentów w katalogu projektu (tam gdzie znajduje się plik phpunit.xml.dist
) spowoduje uruchomienie PHPUnit dokładnie w taki sposób jak określono w konfiguracji przedstawionej powyżej. Teraz możemy dodać cel do pliku build.xml
, który wywoła PHPUnit do przeprowadzenia testów jednostkowych.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<target name="phpunit" unless="phpunit.done" depends="prepare" description="Run unit tests with PHPUnit"> <exec executable="${phpunit}" resultproperty="result.phpunit" taskname="phpunit"> <arg value="--configuration"/> <arg path="${basedir}/build/phpunit.xml"/> </exec> <property name="phpunit.done" value="true"/> </target> <target name="phpunit-no-coverage" unless="phpunit.done" depends="prepare" description="Run unit tests with PHPUnit (without generating code coverage reports)"> <exec executable="${phpunit}" failonerror="true" taskname="phpunit"> <arg value="--configuration"/> <arg path="${basedir}/build/phpunit.xml"/> <arg value="--no-coverage"/> </exec> <property name="phpunit.done" value="true"/> </target> |
Przy okazji dodajmy dwa inne cele. Clean to target odpowiedzialny za czyszczenie (usuwanie) artefaktów wyprodukowanych przez poprzedni build. Ten cel uruchamiany jest przez target prepare, ale może być także wywoływany niezależnie.
1 2 3 4 5 6 7 8 9 10 |
<target name="clean" unless="clean.done" description="Cleanup build artifacts"> <delete dir="${basedir}/build/api"/> <delete dir="${basedir}/build/coverage"/> <delete dir="${basedir}/build/logs"/> <delete dir="${basedir}/build/pdepend"/> <delete dir="${basedir}/build/phpdox"/> <property name="clean.done" value="true"/> </target> |
Cel prepare uruchamia target clean i tworzy katalogi, w których PHPUnit zapisuje logi i raporty.
1 2 3 4 5 6 7 8 9 10 11 |
<target name="prepare" unless="prepare.done" depends="clean" description="Prepare for build"> <mkdir dir="${basedir}/build/api"/> <mkdir dir="${basedir}/build/coverage"/> <mkdir dir="${basedir}/build/logs"/> <mkdir dir="${basedir}/build/pdepend"/> <mkdir dir="${basedir}/build/phpdox"/> <property name="prepare.done" value="true"/> </target> |
Cała zawartość build.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
<?xml version="1.0" encoding="UTF-8"?> <project name="name-of-project" default="full-build"> <!-- By default, we assume all tools to be on the $PATH --> <property name="pdepend" value="pdepend"/> <property name="phpcpd" value="phpcpd"/> <property name="phpcs" value="phpcs"/> <property name="phpdox" value="phpdox"/> <property name="phploc" value="phploc"/> <property name="phpmd" value="phpmd"/> <property name="phpunit" value="phpunit"/> <!-- Use this when the tools are located as PHARs in ${basedir}/build/tools <property name="pdepend" value="${basedir}/build/tools/pdepend.phar"/> <property name="phpcpd" value="${basedir}/build/tools/phpcpd.phar"/> <property name="phpcs" value="${basedir}/build/tools/phpcs.phar"/> <property name="phpdox" value="${basedir}/build/tools/phpdox.phar"/> <property name="phploc" value="${basedir}/build/tools/phploc.phar"/> <property name="phpmd" value="${basedir}/build/tools/phpmd.phar"/> <property name="phpunit" value="${basedir}/build/tools/phpunit.phar"/> --> <!-- Use this when the tools are managed by Composer in ${basedir}/vendor/bin <property name="pdepend" value="${basedir}/vendor/bin/pdepend"/> <property name="phpcpd" value="${basedir}/vendor/bin/phpcpd"/> <property name="phpcs" value="${basedir}/vendor/bin/phpcs"/> <property name="phpdox" value="${basedir}/vendor/bin/phpdox"/> <property name="phploc" value="${basedir}/vendor/bin/phploc"/> <property name="phpmd" value="${basedir}/vendor/bin/phpmd"/> <property name="phpunit" value="${basedir}/vendor/bin/phpunit"/> --> <target name="full-build" depends="prepare,static-analysis,phpunit,phpdox,-check-failure" description="Performs static analysis, runs the tests, and generates project documentation"/> <target name="full-build-parallel" depends="prepare,static-analysis-parallel,phpunit,phpdox,-check-failure" description="Performs static analysis (executing the tools in parallel), runs the tests, and generates project documentation"/> <target name="quick-build" depends="prepare,lint,phpunit-no-coverage" description="Performs a lint check and runs the tests (without generating code coverage reports)"/> <target name="static-analysis" depends="lint,phploc-ci,pdepend,phpmd-ci,phpcs-ci,phpcpd-ci" description="Performs static analysis" /> <!-- Adjust the threadCount attribute's value to the number of CPUs --> <target name="static-analysis-parallel" description="Performs static analysis (executing the tools in parallel)"> <parallel threadCount="2"> <sequential> <antcall target="pdepend"/> <antcall target="phpmd-ci"/> </sequential> <antcall target="lint"/> <antcall target="phpcpd-ci"/> <antcall target="phpcs-ci"/> <antcall target="phploc-ci"/> </parallel> </target> <target name="clean" unless="clean.done" description="Cleanup build artifacts"> <delete dir="${basedir}/build/api"/> <delete dir="${basedir}/build/coverage"/> <delete dir="${basedir}/build/logs"/> <delete dir="${basedir}/build/pdepend"/> <delete dir="${basedir}/build/phpdox"/> <property name="clean.done" value="true"/> </target> <target name="prepare" unless="prepare.done" depends="clean" description="Prepare for build"> <mkdir dir="${basedir}/build/api"/> <mkdir dir="${basedir}/build/coverage"/> <mkdir dir="${basedir}/build/logs"/> <mkdir dir="${basedir}/build/pdepend"/> <mkdir dir="${basedir}/build/phpdox"/> <property name="prepare.done" value="true"/> </target> <target name="lint" unless="lint.done" description="Perform syntax check of sourcecode files"> <apply executable="php" taskname="lint"> <arg value="-l" /> <fileset dir="${basedir}/src"> <include name="**/*.php" /> <modified /> </fileset> <fileset dir="${basedir}/tests"> <include name="**/*.php" /> <modified /> </fileset> </apply> <property name="lint.done" value="true"/> </target> <target name="phploc" unless="phploc.done" description="Measure project size using PHPLOC and print human readable output. Intended for usage on the command line."> <exec executable="${phploc}" taskname="phploc"> <arg value="--count-tests" /> <arg path="${basedir}/src" /> <arg path="${basedir}/tests" /> </exec> <property name="phploc.done" value="true"/> </target> <target name="phploc-ci" unless="phploc.done" depends="prepare" description="Measure project size using PHPLOC and log result in CSV and XML format. Intended for usage within a continuous integration environment."> <exec executable="${phploc}" taskname="phploc"> <arg value="--count-tests" /> <arg value="--log-csv" /> <arg path="${basedir}/build/logs/phploc.csv" /> <arg value="--log-xml" /> <arg path="${basedir}/build/logs/phploc.xml" /> <arg path="${basedir}/src" /> <arg path="${basedir}/tests" /> </exec> <property name="phploc.done" value="true"/> </target> <target name="pdepend" unless="pdepend.done" depends="prepare" description="Calculate software metrics using PHP_Depend and log result in XML format. Intended for usage within a continuous integration environment."> <exec executable="${pdepend}" taskname="pdepend"> <arg value="--jdepend-xml=${basedir}/build/logs/jdepend.xml" /> <arg value="--jdepend-chart=${basedir}/build/pdepend/dependencies.svg" /> <arg value="--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg" /> <arg path="${basedir}/src" /> </exec> <property name="pdepend.done" value="true"/> </target> <target name="phpmd" unless="phpmd.done" description="Perform project mess detection using PHPMD and print human readable output. Intended for usage on the command line before committing."> <exec executable="${phpmd}" taskname="phpmd"> <arg path="${basedir}/src" /> <arg value="text" /> <arg path="${basedir}/build/phpmd.xml" /> </exec> <property name="phpmd.done" value="true"/> </target> <target name="phpmd-ci" unless="phpmd.done" depends="prepare" description="Perform project mess detection using PHPMD and log result in XML format. Intended for usage within a continuous integration environment."> <exec executable="${phpmd}" taskname="phpmd"> <arg path="${basedir}/src" /> <arg value="xml" /> <arg path="${basedir}/build/phpmd.xml" /> <arg value="--reportfile" /> <arg path="${basedir}/build/logs/pmd.xml" /> </exec> <property name="phpmd.done" value="true"/> </target> <target name="phpcs" unless="phpcs.done" description="Find coding standard violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing."> <exec executable="${phpcs}" taskname="phpcs"> <arg value="--standard=PSR2" /> <arg value="--extensions=php" /> <arg value="--ignore=autoload.php" /> <arg path="${basedir}/src" /> <arg path="${basedir}/tests" /> </exec> <property name="phpcs.done" value="true"/> </target> <target name="phpcs-ci" unless="phpcs.done" depends="prepare" description="Find coding standard violations using PHP_CodeSniffer and log result in XML format. Intended for usage within a continuous integration environment."> <exec executable="${phpcs}" output="/dev/null" taskname="phpcs"> <arg value="--report=checkstyle" /> <arg value="--report-file=${basedir}/build/logs/checkstyle.xml" /> <arg value="--standard=PSR2" /> <arg value="--extensions=php" /> <arg value="--ignore=autoload.php" /> <arg path="${basedir}/src" /> <arg path="${basedir}/tests" /> </exec> <property name="phpcs.done" value="true"/> </target> <target name="phpcpd" unless="phpcpd.done" description="Find duplicate code using PHPCPD and print human readable output. Intended for usage on the command line before committing."> <exec executable="${phpcpd}" taskname="phpcpd"> <arg path="${basedir}/src" /> </exec> <property name="phpcpd.done" value="true"/> </target> <target name="phpcpd-ci" unless="phpcpd.done" depends="prepare" description="Find duplicate code using PHPCPD and log result in XML format. Intended for usage within a continuous integration environment."> <exec executable="${phpcpd}" taskname="phpcpd"> <arg value="--log-pmd" /> <arg path="${basedir}/build/logs/pmd-cpd.xml" /> <arg path="${basedir}/src" /> </exec> <property name="phpcpd.done" value="true"/> </target> <target name="phpunit" unless="phpunit.done" depends="prepare" description="Run unit tests with PHPUnit"> <exec executable="${phpunit}" resultproperty="result.phpunit" taskname="phpunit"> <arg value="--configuration"/> <arg path="${basedir}/build/phpunit.xml"/> </exec> <property name="phpunit.done" value="true"/> </target> <target name="phpunit-no-coverage" unless="phpunit.done" depends="prepare" description="Run unit tests with PHPUnit (without generating code coverage reports)"> <exec executable="${phpunit}" failonerror="true" taskname="phpunit"> <arg value="--configuration"/> <arg path="${basedir}/build/phpunit.xml"/> <arg value="--no-coverage"/> </exec> <property name="phpunit.done" value="true"/> </target> <target name="phpdox" unless="phpdox.done" depends="phploc-ci,phpcs-ci,phpmd-ci" description="Generate project documentation using phpDox"> <exec executable="${phpdox}" dir="${basedir}/build" taskname="phpdox"/> <property name="phpdox.done" value="true"/> </target> <target name="-check-failure"> <fail message="PHPUnit did not finish successfully"> <condition> <not> <equals arg1="${result.phpunit}" arg2="0"/> </not> </condition> </fail> </target> </project> |
- full-build – to domyślny target. Uruchamia on wszystkie narzędzia w taki sposób, że logi XML pisane są do domyślnych lokalizacji. Full-build powinien być przeprowadzany w nocy ze względu na długi czas wykonywania.
- full-build-paralel – robi to samo co full-build z tą różnicą, że statyczne narzędzia analizy uruchamiane są równolegle. Nawet na maszynie z wieloma procesorami pełny build może trwać dużo czasu dlatego ten target powinien być uruchamiany również w nocy.
- quick-build – target uruchamiany przez “dżoby” wyzwalane w czasie wgrywania nowego kodu do repozytorium.
- clean – używany do kasowania artefaktów (np. logów i innych rzeczy generowanych w czasie buildu).
- lint – używany do testowania składni kodu projektu (php -l). To zadanie może być używane przed commitem.
- phpcs – używany do odnajdywania naruszeń standardu kodowania. Wyświetla wyniki czytelne dla człowieka. To zadanie może być używane przed commitem.
- phpcpd – używany do odnajdywania zduplikowanego kodu. Wyświetla wyniki czytelne dla człowieka. To zadanie może być używane przed commitem.
- phpmd – używany do odnajdywania bałaganu w projekcie. Wyświetla wyniki czytelne dla człowieka. To zadanie może być używane przed commitem.
- phpunit – używany do uruchamiania testów jednostkowych. To zadanie może być używane przed commitem.
- phpdox – używany do generowania dokumentacji projektu.
- phploc – używany do obliczania rozmiaru projektu.
Uruchomienie build.xml spowoduje wygenerowanie katalogu o poniższej zawartości:
1 2 3 4 5 6 7 8 9 10 11 12 |
build |-- api ... |-- coverage ... `-- logs |-- checkstyle.xml |-- clover.xml |-- crap4j.xml |-- jdepend.xml |-- junit.xml |-- phploc.csv |-- pmd-cpd.xml `-- pmd.xml |
Artefakty te będą następnie przetwarzane przez Jenkinsa.
Ze strony http://jenkins-php.org pobieramy szablon nowego “job” Jenkinsa. Katalog domowy Jenkinsa ($JENKINS_HOME
) to w systemach RedHat-opodobnych /var/lib/jenkins
:
1 2 3 4 5 6 |
cd $JENKINS_HOME/jobs mkdir php-template cd php-template wget https://raw.github.com/sebastianbergmann/php-jenkins-template/master/config.xml cd .. chown -R jenkins:jenkins php-template/ |
Przeładowujemy konfigurację Jenkinsa:
1 |
java -jar jenkins-cli.jar -s http://localhost:8080 reload-configuration |
W przypadku błędów przy próbie przeładowania można zrestartować serwer Jenkinsa. Następnie w Jenkinsie:
- Klikamy “New Item”.
- Wpisujemy nazwę w polu “Enter an item name”.
- W polu “Copy from” wpisujemy “php-template”.
- Klikamy “OK”.
- Odznaczamy (odhaczamy) opcję “Disable Build”.
- Wpisujemy URL do projektu PHP na GIT w “Source Code Management”.
- Konfigurujemy “Build Trigger” na “Poll SCM”.
- Klikamy “Save”.