phpunit使い方からxdebug導入によるcoverage出力方法まで

みなさんテスト書いてますか？
私、フリーランス兼プログラマー兼経営者として4年ぐらい経つのですが唯一経験していない工程があります。
それがテスト工程（プログラムの領域でのテスト）です。

上流下流工程を全てやってきましたが、唯一...唯一スキルシートに○をつけられない工程がテストでした。
（全てじゃないじゃん。）

何のためにテスト書くのか意味がわかりませんでした。
だって、本番反映する前の環境で、リリース内容物確認や開発チーム内で動作確認会みたいなのするでしょう？

今のご時世コロナ禍でMTGなどで口頭で伝えて確認するやり方もありますでしょうが、だいたいは「動作確認指示書」なるものがあると思います。
それに沿って操作を行い期待した動作をしているかなどを行なって問題なければリリースできるようなフローが一般的かと思います。

これと同じことをコードによるテスト管理するのってコストが二重にかかっていると思いませんか？
どういった形式でテストをやるかの違いだけのような気がするのは私だけでしょうか？

コードによるテスト管理をするのであれば、動作確認会みたいなのはいらないと思うのです。
逆に、動作確認会みたいなのを行うのであれば、コードによるテスト管理はしなくてもいいと思うんです。
勿論、どっちもするに越したことはないのは間違いないです。

ただ、生産性やtime is money的な観点から見ると私はコードによるテスト管理はコストでしかないと思うのです。
何故なら、仕様を把握しつつユーザー側とやりとりしてるのって大体はCS業務の方々じゃないですか。
プログラマー側としてはコーディングのみに注力してチケット回転率高めていった方がよっぽど生産性高いと思いませんか？

だから、チケット管理する前にPOはCS側から要望要件吸い上げて、そこからチーム内で叩き上げていってこの仕様で良いか？ってのをCS側とすり合わせておいた上で、PO側（開発側サイド）が仕様書を書き起こし、チケットからはその仕様書を参照するような形にします。
そうすれば、プログラマー側としてはコーディングのみに注力していくフローが確立しつつ、STG環境などで動作確認を依頼されるであろうCS側としても仕様書を参照して作られるであろう「動作確認指示書」を参照し操作していくことが可能となります。

このフローを確立するにあたってウォーターフォールでもアジャイルでも、だいたいは機能単位でリリース設定しスプリントしていくことには変わりはないので開発手法は関係ないと思っています。
アジャイルでも年単位とか全体の長い目で見ると**部分的なウォーターフォール**なんです。

**「いや〜うちはアジャイルだから動作確認とか仕様書とかもその都度柔軟に対応してるんですよね〜」**

っていう現場はたいだい四苦八苦してるか生産性が望ましくない状態にあると思います。
テストを書いててもだいたいはケースを網羅できず動作確認会などで漏れが発覚し、チケットが貯まりやすい傾向にあると思います。

だからなぜコードによるテストが必要なのかがわからなかったのです。

CS側からエンジニア側にこの仕様であってますよね？って確認とか入るのってすごく非効率だと思っていました。
とは言え、まぁ人間の脳もバグを起こしますから、プログラムでエビデンス取って安心しておきたいっていう側面も理解はできます。

しかし、エビデンス取って安心しておきたいことが目的ならやはり、誰もが見やすい仕様書を制定し仕様書が間違いないかのエビデンスを取る方が重要ではないのかなと私は考えます。


**「経営者エンジニアは品質よりも結果を優先させたいからそんな風に考えられるんだ」**
**「会社員エンジニアは経営者とは違ってプログラムに関して繊細だしテストは書いときたいもんなんだよ」**


私は基本的に前者ですが後者も分かります。

何故なら、テスト工程を経験したことのない私が分からないなりにphpunitを導入しcoverageを計測し、100%になっていないテストやそもそも作られていないテストがあることを見ると、**100%にしてやりたいという血が騒いだからです。**

基本的に前者であることに変わりはない私ですが、だいぶ前置きが長くなってしまったのでphpunit簡単な使い方と恐らくプチ詰まりするであろうカバレッジ計測する為に必要なものについて紹介していこうと思います。

```shell
# 全テスト実行
./vendor/bin/phpunit
# 指定したテスト実行
./vendor/bin/phpunit tests/Feature/Controllers/Auth/XxxTest.php
# 指定したテストの指定したケース実行
./vendor/bin/phpunit tests/Feature/Controllers/Auth/XxxTest.php --filter testIndex
# 全テスト実行 + カバレッジ計測結果出力もする
./vendor/bin/phpunit --coverage-html=".code-coverage"
```

最後のコマンド実行すると`No code coverage driver available`という警告がでてくると思う。

```shell
[root@d171feeff4ee:0 html]# ./vendor/bin/phpunit --coverage-html=".code-coverage"
PHPUnit 9.5.20 #StandWithUkraine

Warning:       No code coverage driver available
```

ちょっと調べればわかるが、xdebugかPHPDBGがないことが原因だ。

https://phpunit.readthedocs.io/en/9.5/code-coverage-analysis.html

ここから先はそれぞれの開発環境が異なると思うが、dockerとxdebugで説明する。

私の場合はlaravelに同梱されているphpunintを使ってTestを実施している。
そしてそのlaravelを動かす環境はdockerで動かしていて、イメージはamazonlinux2を使っており、その中でxdebugをinstallしている。

```Dockerfile
FROM amazonlinux:2

RUN yum update -y

RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
    echo -e 'ZONE="Asia/Tokyo"\nUTC=true' | tee /etc/sysconfig/clock \
    echo "LANG=ja_JP.UTF-8" | tee /etc/sysconfig/i18n

RUN yum update -y amazon-linux-extras \
    && amazon-linux-extras install -y vim php8.0 epel nginx1 python3.8 \
    && yum install -y php php-devel php-mbstring php-xml php-pear gcc zip unzip \
    && pecl install xdebug \
    && yum install -y php-gd.x86_64 php-imagick \
    && pip3.8 install supervisor \
    && rm -rf /var/cache/yum/* \
    && yum clean all \
    && php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/local/bin/ --filename=composer

RUN chmod 777 /var/lib/php/session
COPY ./etc /etc

CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]

```


`pecl install xdebug`した結果、以下のようにphp.iniのzend_extensionにpathを追加してくれよなってアドバイスしてくれる。

```
Build process completed successfully
Installing '/usr/lib64/php/modules/xdebug.so'
install ok: channel://pecl.php.net/xdebug-3.1.4
configuration option "php_ini" is not set to php.ini location
You should add "zend_extension=/usr/lib64/php/modules/xdebug.so" to php.ini
```

phpinfo()でブラウザ確認でも良いが、php -iでコンソール確認が早い。
以下の部分があればinstallはOK。

```
xdebug

__   __   _      _
\ \ / /  | |    | |
 \ V / __| | ___| |__  _   _  __ _
  > < / _` |/ _ \ '_ \| | | |/ _` |
 / . \ (_| |  __/ |_) | |_| | (_| |
/_/ \_\__,_|\___|_.__/ \__,_|\__, |
                              __/ |
                             |___/

Version => 3.1.4
Support Xdebug on Patreon, GitHub, or as a business: https://xdebug.org/support

             Enabled Features (through 'xdebug.mode' setting)
Feature => Enabled/Disabled
Development Helpers => ✔ enabled
Coverage => ✘ disabled
GC Stats => ✘ disabled
Profiler => ✘ disabled
Step Debugger => ✘ disabled
Tracing => ✘ disabled
```

ローカルにphp.ini作っておくと何かと楽。設定変更時に便利。

```ini
# ...このあたりで1650行目ぐらい
; xdebugは必要な時にextensionをコメントアウトして再起動して利用
zend_extension=/usr/lib64/php/modules/xdebug.so
[xdebug]
xdebug.idekey="xxx-api"
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.remote_connect_back = Off
xdebug.remote_host = "host.docker.internal"
xdebug.remote_port=9001
```

ちなみにamazonlinux内の構造と同じ構造でローカルに持っている。

```
etc
  nginx
    conf.d
      default.conf
    nginx.conf
  supervisor
    conf.d
      nginx.supervisor.conf
      php-fpm.supervisor.conf
    supervisord.conf
  php.ini
  php-fpm.conf
  www.conf
```

これをDockerfileで`COPY ./etc /etc`してるだけなので、勝手に良い感じに設定ファイルを読み込んでくれる。
docker-compose.ymlでマウントさせる作りにしても良さそうだが、適宜よしなにやってくれればいいと思う。

xdebugも導入できたしもう一度バレッジ計測結果出力してみるかーと実行するも、また新たなる警告が。

```shell
[root@d171feeff4ee:0 html]# ./vendor/bin/phpunit --coverage-html=".code-coverage"
PHPUnit 9.5.20 #StandWithUkraine

Warning:       XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set
```

どっちか追加しなはれと言われてますが、envの方を追加しても、以下のようにphp.iniにも追加しなはれと言われるので、docker環境の方を修正します。ちなみにenvは追加しなくても大丈夫だったので追加していません。

```shell
[root@d171feeff4ee:0 html]# ./vendor/bin/phpunit --coverage-html=".code-coverage"
PHPUnit 9.5.20 #StandWithUkraine

Code coverage needs to be enabled in php.ini by setting 'xdebug.mode' to 'coverage'
```

php.iniの設定変更なのでcontainerは再起動しないと反映されないです。


```shell
docker-compose restart
```


```shell
[root@d171feeff4ee:0 html]# ./vendor/bin/phpunit --coverage-html=".code-coverage"
PHPUnit 9.5.20 #StandWithUkraine

.
seeding time：0.72505402565002441406 seconds
.......                                                            8 / 8 (100%)

Time: 00:07.440, Memory: 46.50 MB

OK (8 tests, 14 assertions)

Generating code coverage report in HTML format ... done [00:00.255]
```

出力ファイルはdocker環境に依存しているわけではないので絶対pathでindex.htmlなどを開けば、何が100%で何が不足しているのかが一目瞭然になります。

![スクリーンショット 2022-05-01 18.34.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125469/2e42ffa8-e420-f229-1cb4-b4e7b70ae418.png)




こうして晴れて~~テスト信者の仲間入りを果たした~~スキルシートのテスト工程に◯が付けられるようになった私なのであった。
ただ、エンジニアによってはテストケースの網羅が人それぞれになることは普通にあることだと思うので、どういったテストが行われていることが期待値なのかをチケットに記載することは徹底したいししてもらいたい。