VSCode の Marketplace で PHPUnit で検索して出てきた中からダウンロード数が多い 3 つの拡張を使ってみました。
なお、普段は↓のようなめんどくさい環境でコードを書いています。
- Windows で PhpStorm を実行している
- PHP は WSL 上の docker-compse コマンドを使って Docker 環境で実行している
- direnv で環境変数をいろいろ設定している
ので同様に VSCode も Windows 上から WSL 上で direnv を適用しつつ docker-compse を使いたいです。
emallin.phpunit
デフォで vendor/bin/phpunit
とか vendor/phpunit/phpunit/phpunit
とか phpunit*.phar
とかを再帰的に検索して php
コマンドで実行してくれます。vendor/bin/phpunit
決め打ちじゃないのと Windows の場合に vendor/phpunit/phpunit/phpunit
を探してくれるのは便利です。Phar もプロジェクトディレクトリ内に置いとけば自動で見つけてくれます。
リモート実行する場合、次のように任意のコマンドが指定できるので、Windows 上から WSL の docker-compose を実行すれば OK です。テストファイルを指定して実行するときのために phpunit.paths
も指定する必要があります。
{ "phpunit.command": "wsl direnv exec . docker-compose run --rm app", "phpunit.phpunit": "vendor/bin/phpunit", "phpunit.paths": { "${workspaceFolder}": "/app", } }
SSH や Docker で実行するための別の設定 phpunit.ssh
とか phpunit.docker.container
とか docker.image
とかもありますが、phpunit.command
だけで指定するほうが簡単なんじゃないかと思います。
メソッドを指定して実行するときにメソッド名が単純に --filter
に渡されるので、例えば hoge
というメソッドのテストを実行するときに hogehoge
も一緒に実行されてしまいます。変に頑張られると Windows と Linux でのシェルのクオートやエスケープの違いでうまく動かなくなったりするので、これでも十分かなと言う気もします。
phpunit.args
設定で PHPUnit のコマンドラインオプションを配列で指定できます。配列なので適切なエスケープが施されるか、あるいはシェルを経由しないのかと思いきや、実際には単純に join でスペース区切りで繋げられてシェル経由で実行されます。これ配列である必要全然無いような。。。むしろ phpunit.command
phpunit.phpunit
phpunit.args
をまとめて1つの文字列の設定でも十分な気もします。実際のところ次のように適当にバラけさせても動きます。
{ "phpunit.command": "wsl direnv", "phpunit.phpunit": "exec .", "phpunit.args": ["docker-compose", "run", "--rm", "app", "vendor/bin/phpunit"], "phpunit.paths": { "${workspaceFolder}": "/app", }, }
リモート実行のために phpunit.paths
でパスのマッピングをしているとき、テストがコケたときに赤線がついたり問題タブに表示されたりするのが機能しなくなることがあります。下記で /app
がハードコードされているためなので Docker 内では /app
にソースを配置すれば大丈夫です。
PHPUnit は拡張が直接実行しているわけではなく、VSCode の ShellExecution タスクとして実行するようになっています。problemMatchers
という VSCode の拡張ポイントを使って実行結果から正規表現でコケたテストケースのファイルや行番号やメッセージを抜き出すようになっているようです。この problemMatchers
は動的にはできなさそうなのでこのような動作になっているようです。
calebporzio.better-phpunit
デフォだと vendor/bin/phpunit
が(Windows なら vendor/bin/phpunit.bat
)が決め打ちで実行されます。
better-phpunit.phpunitBinary
でコマンドは変更できます。これはシェルで解釈されるので Windows なら php vendor/phpunit/phpunit/phpunit
とかでも OK です。
Docker などでリモート実行するときは次のようにコマンドを指定します。ローカルとリモートのファイル名のマッピングで ${workspaceFolder}
は使用できないので次のように絶対パスで指定する必要があります。なお、Windows でもパス区切りは \\
ではなく /
で指定します。
{ "better-phpunit.phpunitBinary": "vendor/bin/phpunit", "better-phpunit.docker.enable": true, "better-phpunit.docker.command": "wsl direnv exec . docker-compose run --rm app", "better-phpunit.docker.paths": { "c:/Users/oreore/path/to/project": "/app" }, }
better-phpunit.docker.command
と better-phpunit.phpunitBinary
は単にスペース区切りで連結されるだけです。なので次のように適当にバラしても通ります。
{ "better-phpunit.docker.enable": true, "better-phpunit.docker.command": "wsl direnv exec .", "better-phpunit.phpunitBinary": "docker-compose run --rm app vendor/bin/phpunit", "better-phpunit.docker.paths": { "c:/Users/oreore/path/to/project": "/app" }, }
設定の項目名に docker
と付いていますが Docker でなくても動きます。例えば次のようにすれば Windows 上の VSCode から WSL 上の PHPUnit を実行できます。
{ "better-phpunit.docker.enable": true, "better-phpunit.docker.command": "wsl php", "better-phpunit.phpunitBinary": "vendor/bin/phpunit", "better-phpunit.docker.paths": { "c:/Users": "/c/Users" }, }
次のようにすればローカルの Windows 上の PHP が実行されます。つまり最初の better-phpunit.phpunitBinary
だけ指定したときと同じです。
{ "better-phpunit.docker.enable": true, "better-phpunit.docker.command": "php", "better-phpunit.phpunitBinary": "vendor/phpunit/phpunit/phpunit", }
コケたテストに赤線をつけたり問題タブに表示したりする機能が機能していないようです。たぶん package.json
の problemMatchers
のパターンが間違っているためだと思います。また、仮に problemMatchers
が修正されたとしても「リモートの実行結果のパス → ローカルのパス」のマッピングはサポートしてなさそうなので、リモート実行だと赤線や問題タブには表示されません。
なお、emallin.phpunit と同様で PHPUnit は拡張が直接実行しているわけではなく、VSCode の ShellExecution タスクとして実行するようになっています。problemMatchers という拡張ポイントを使って実行結果から正規表現でコケたテストケースのファイルや行番号やメッセージを抜き出すようになっているようです。
recca0120.vscode-phpunit
サイドバーにテストのエクスプローラーが表示できたり、CodeLenses でコード上からサクッと実行できたり、多機能です。
前述の 2 つと比べるとだいぶ複雑です。内部では LanguageServer を実行していて PHPUnit は LanguageServer から child_process.spawn
で直接実行されています。コケたテストのファイル名や行番号やメッセージは PHPUnit で出力した JUnit XML から抜き出しているようです。
素のままだとデフォで vendor/bin/phpunit
(Windows なら vendor/bin/phpunit.bat
)を実行します。設定の phpunit.phpunit
で変更できますが、絶対パスで指定する必要があります。相対パスで指定しても頭に /
が付けられてしまいます。
phpunit.php
で PHP のバイナリを指定すれば PHPUnit のコマンドを直接ではなく明示的に PHP コマンドで実行されるようになります。Windows で Phar を指定するときに使えそうです。なお、これも絶対パスで指定する必要があります。パスが通ったところにあるコマンドを指定しても頭に /
が付けられるのでダメです。
phpunit.args
で PHPUnit のコマンドラインオプションが指定できます。phpunit.args
に -c
が含まれないときは phpunit.xml.dist
または phpunit.xml
が検索され、見つかればコマンドラインに自動で追加されます。これも絶対パスで追加されます。
テストファイルを指定して実行するとき、phpunit.relativeFilePath: true
に設定されていると相対パスで指定されるようになります。リモート実行するときはローカルの絶対パスが指定されてもダメなのでこのオプションが必須です。ただし前述の phpunit.xml
が自動で追加されるときは絶対パスのままなので、リモート実行させるなら phpunit.args
で -c
オプションを指定する必要があります。
Windows で実行するとき、パス区切り文字はバックスラッシュのままです。なので Windows から Docker などでリモート実行させるのは難しいです。
phpunit.shell
でシェルを経由するかどうかが指定できます。これはそのまま child_process.spawn
のオプションになります。
カーソル位置のテストを実行や、テストエクスプローラーや CodeLenses で単一のメソッドのテストを実行する機能が機能していません。たぶん下記の問題だと思いますが・・
WSL 上で VSCode を実行しているならこれで解決します。
{ "phpunit.shell": "bash" }
Windows 上で VSCode を実行しているときはどうしようもありません。不便なので治しておきました。
もうマージされてリリースされているようなのでこの問題は解決していると思います。
さいごに
recca0120.vscode-phpunit が高機能で IDE っぽい感じで良さそうですが、Windows 上の VSCode からのリモート実行が絶望的です。Remote Development とかで Docker 内で VSCode を動かせばいいんでしょうけど。
emallin.phpunit は Docker 内で /app
にコードを置く必要がある、という問題はありますが・・それさえ目をつぶれば一番安定して使えそうです。
calebporzio.better-phpunit はコケたテストの赤線や問題タブが機能しないので微妙です。それ以外は動作原理は emallin.phpunit とほぼほぼ同じで違いがあまりありません。