経緯
Docker 上に Ubuntu + PHP8.1 のコンテナ環境を構築して、そこで PHPUnit のコードカバレッジを得るまでに紆余曲折あったのでメモ。
前提
まず前提として、 Dockerfile
や compose.yml
は以下の記述になっています。
compose.yml
version: '3.9'
services:
web:
build:
context: ./apache/docker
dockerfile: Dockerfile
args:
WEB_ROOT_DIRECTORY: $WEB_ROOT_DIRECTORY
WEB_CONTAINER_PORTNUM: $WEB_CONTAINER_PORTNUM
PHP_VERSION: $PHP_VERSION
volumes:
# workspace
- ./workspace:/workspace
# docker settings template
- ./template:/template
# apache log
- ./apache/log:/var/log/apache2
# php ini
- ./php/ini/php.ini:/etc/php/${PHP_VERSION}/fpm/php.ini
# error log
- ./php/error_log:/var/log/php
# SSL
- ./cert:/etc/ssl/private
tty: true
ports:
- "$WEB_HOST_PORTNUM:$WEB_CONTAINER_PORTNUM"
- "$WEB_HOST_PORTSSL:$WEB_CONTAINER_PORTSSL"
entrypoint: bash -c "bash /workspace/entrypoint_web.sh $WEB_ROOT_DIRECTORY $WEB_DOMAIN $WEB_HOST_PORTNUM $WEB_CONTAINER_PORTNUM $WEB_HOST_PORTSSL $WEB_CONTAINER_PORTSSL $PHP_VERSION && /bin/bash"
Dockerfile
FROM ubuntu:latest
# args
ARG WEB_ROOT_DIRECTORY
ARG WEB_CONTAINER_PORTNUM
ARG PHP_VERSION
# install tzdata without interactive
ENV DEBIAN_FRONTEND=noninteractive
# apt update & install
RUN apt update \
&& apt install -y sudo \
vim \
less \
procps \
# network ss (instaed of netstat)
iproute2 \
# apache
apache2 \
# zip
zip \
unzip \
# SSL
openssl \
# git
git \
# pasword
passwd \
# Set Timezone
tzdata \
# Set local to jp.
language-pack-ja \
&& update-locale LANG=ja_JP.UTF-8 \
# cleaning
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
# set env
ENV TZ="Asia/Tokyo" \
LANG="ja_JP.UTF-8" \
LANGUAGE="ja_JP:ja" \
LC_ALL="ja_JP.UTF-8"
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
# install php
RUN apt update \
&& apt install -y php \
libapache2-mod-php \
php-fpm \
php-common \
php-mbstring \
php-xmlrpc \
php-gd \
php-xml \
php-cli \
php-zip \
php-curl \
php-imagick \
# SQLite
php-sqlite3 \
# MySQL
#php-pdo \
#php-mysql \
# cleaning
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
# composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
# PHP
RUN mkdir -p /run/php
RUN chown www-data:www-data /run/php
# php log
RUN mkdir /var/log/php
RUN chown www-data /var/log/php
RUN chmod 755 /var/log/php
# SSL
RUN mkdir -p /etc/ssl/private
# volume directory
RUN mkdir -p /template
RUN mkdir -p /var/www/${WEB_ROOT_DIRECTORY}
RUN mkdir -p /var/www/${WEB_ROOT_DIRECTORY}/web
RUN mkdir -p /workspace
entrypoint_web.sh
#!/bin/bash
# gen key & certificate
## localhost
openssl req -new -newkey rsa:2048 -nodes \
-out /etc/pki/tls/certs/localhost.csr \
-keyout /etc/pki/tls/private/localhost.key \
-subj "/C=/ST=/L=/O=/OU=/CN=www.example.com"
openssl x509 -days 365 -req \
-signkey /etc/pki/tls/private/localhost.key \
-in /etc/pki/tls/certs/localhost.csr \
-out /etc/pki/tls/certs/localhost.crt
## .env domain
openssl req -new -newkey rsa:2048 -nodes \
-out /etc/ssl/private/server.csr \
-keyout /etc/ssl/private/server.key \
-subj "/C=/ST=/L=/O=/OU=/CN=*.${2}"
openssl x509 -days 365 -req \
-signkey /etc/ssl/private/server.key \
-in /etc/ssl/private/server.csr \
-out /etc/ssl/private/server.crt
# setting file replace and copy
sed -e "s/WEB_ROOT_DIRECTORY/${1}/gi" \
-e "s/WEB_DOMAIN/${2}/gi" \
-e "s/WEB_HOST_PORTNUM/${3}/gi" \
-e "s/WEB_CONTAINER_PORTNUM/${4}/gi" \
-e "s/WEB_HOST_PORTSSL/${5}/gi" \
-e "s/WEB_CONTAINER_PORTSSL/${6}/gi" \
/template/apache/apache_vh.conf > /etc/apache2/sites-available/${1}.conf
sed -e "s/WEB_ROOT_DIRECTORY/${1}/gi" \
-e "s/WEB_DOMAIN/${2}/gi" \
-e "s/WEB_HOST_PORTNUM/${3}/gi" \
-e "s/WEB_CONTAINER_PORTNUM/${4}/gi" \
-e "s/WEB_HOST_PORTSSL/${5}/gi" \
-e "s/WEB_CONTAINER_PORTSSL/${6}/gi" \
/template/apache/apache_vh_ssl.conf > /etc/apache2/sites-available/${1}_ssl.conf
cp /template/apache/ssl.conf /etc/apache2/mods-available/ssl.conf
# apache module enable
a2enmod ssl proxy_fcgi setenvif rewrite
# apache conf enable
echo ServerName www.example.com:${4} >> /etc/apache2/conf-available/example.conf
a2enconf example php${11}-fpm
# apache cirtual site enable
a2ensite ${1} ${1}_ssl
# apache service start
service apache2 start
# PHP
/usr/sbin/php-fpm${7} &
php.ini
;; 略
; XDEBUG ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
xdebug.mode=debug
xdebug.client_host.=host.docker.internal
xdebug.client_port=9000
xdebug.remote_handler="dbgp"
このような設定。 PHP は 8.1 で php-fpm で動作させています。
現象と解決方法
この状態で composer.json
に以下のようなスクリプトを記述。
"scripts": {
"start": "php -d variables_order=EGPCS -S localhost:8999 -t ./",
"test": "phpunit",
"test:coverage": "phpunit --coverage-html coverage"
}
これで composer run test:coverage
を実行すると……
ERROR Code coverage driver not available.
と怒られてしまいました。
よくよく見ると、Xdebugそのものをインストールしていないので当然でした。
## 略
# apt update & install
# install php
RUN apt update \
&& apt install -y php \
libapache2-mod-php \
php-fpm \
php-common \
php-mbstring \
php-xmlrpc \
php-gd \
php-xml \
php-cli \
php-zip \
php-curl \
php-imagick \
# SQLite
php-sqlite3 \
# Xdebug
php-xdebug \
# cleaning
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
## 略
これでもう一度コンテナをビルドして PHPUnit を実行。
XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set
今度は違う Warning が出ました。
; XDEBUG ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
xdebug.mode=debug,coverage
xdebug.client_host.=host.docker.internal
xdebug.client_port=9000
xdebug.remote_handler="dbgp"
php.ini
をこのように変更してみます。 phpinfo();
では Xdebug の項目で
Coverage ✔ enabled
Step Debugger ✔ enabled
と出るようになったのできちんと設定を認識していそうですが、実際に PHPUnit を実行すると同じ Warning で引っかかります。
そこで、 composer.json
に次のように追記。
"scripts": {
"start": "php -d variables_order=EGPCS -S localhost:8999 -t ./",
"test": "phpunit",
"test:coverage": "XDEBUG_MODE=coverage phpunit --coverage-html coverage"
}
これでどうでしょうか……と思ったら、また違う Warning が出ました。
No filter is configured, code coverage will not be processed
どうやら phpunit.xml
の構文が古かったので引っかかるようになってしまった模様。
# ./vendor/phpunit/phpunit//phpunit --migrate-configuration
で XMLファイル をコンバートしたところ、漸くカバレッジを得ることができるようになりました。
参考
Ubuntu での Xdebug パッケージノインストール方法
php-xdebug
。
ERROR Code coverage driver not available.
phpunit.xml の構文
- 4. The XML Configuration File — PHPUnit 10.1 Manual
- Laravel パッケージ開発でのテスト環境整備
- php – PHPUnit Code Coverage – Stack Overflow
その他の phpunit.xml の Warning
XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set
- Code coverage does not work with xdebug 3 : WI-57304
- Support XDEBUG_MODE environment variable · Issue #834 · sebastianbergmann/php-code-coverage · GitHub
- composer test 実行時に「… php.ini by setting ‘xdebug.mode’ to ‘coverage’」
- PHPUnitでHTMLコードカバレッジを出すまで(Docker使用) – Qiita
- Xdebugを使ってPHPUnitテストコードのカバレッジを取得する | 株式会社NoSchool
- setting xdebug.coverage_enable=On on command line for PHPunit – Stack Overflow
XDEBUG_MODE=coverage /usr/bin/phpunit
という書き方が composer.json
の記述のヒントに。