CakePHP4.3のフィクスチャファクトリとプラグインのユニットテスト

引き続き、baserCMS５の開発にてCakePHP2系から4系への移行を進めています。

CakePHP4.3 より、これまでのフィクスチャマネージャーが非推奨になり、フィクスチャファクトリの利用が推奨されているようです。
色々と仕組みがややこしく、ドキュメントが少ないので記録しておきます。

- [フィクスチャのアップグレード](https://book.cakephp.org/4/ja/appendices/fixture-upgrade.html)

従来は、テスト実行時にテーブルの作成とデータ登録がされていたのですが、フィクスチャマネージャーを利用しなくなった事により、テーブルの作成処理が `tests/bootstrap.php` でのマイグレーションでの生成に移行となっている様子。  
つまり、 `bootstrap.php` にて、マイグレーションを実行しないとテーブルが作成されない仕様。

まず、マイグレーションは `tests/bootstrap.php` 次のように実行します。
```php
use Migrations\TestSuite\Migrator;
// マイグレーションが一つの場合
new Migrator->run(['plugin' => 'BaserCore']);
// マイグレーションが複数の場合
(new Migrator())->runMany([
    ['plugin' => 'BaserCore'],
    ['plugin' => 'BcFavorite']
]);
```

### bootstrap の実行仕様

となると、プラグインのマイグレーションは、プラグイン内の `bootstrap.php` で実行したいところだが、 `bootstrap.php` の実行は、カレントディレクトリ内の `tests/bootstrap.php` を利用する仕様となっているため、プラグイン内の `bootstrap.php`を実行するは、プラグインディレクトリに移動するか、 `--bootstrap` オプションを指定しなければならない。 めんどい。

```shell
# プラグインディレクトリに移動する場合
cd plugins/baser-core
../../vendor/bin/phpunit
# アプリケーションルートでオプションを指定する場合
vendor/bin/phpunit --bootstrap plugins/baser-core/tests/bootstrap.php
```


　
### プラグイン単独でユニットテストを実行する

その上で、プラグイン単独でユニットテストを実行する方法を調べてみた。  
まず、プラグインディレクトリ内に `phpunit.xml.dist` を作成する。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
         colors="true"
         bootstrap="tests/bootstrap.php"
>
    <php>
        <ini name="memory_limit" value="-1"/>
        <ini name="apc.enable_cli" value="1"/>
    </php>
    <testsuites>
        <testsuite name="BcFavorite">
            <directory>tests/TestCase</directory>
        </testsuite>
    </testsuites>
    <extensions>
        <extension class="Cake\TestSuite\Fixture\PHPUnitExtension"/>
    </extensions>
    <coverage>
        <include>
            <directory suffix=".php">src/</directory>
        </include>
    </coverage>
</phpunit>
```

次に、`tests/bootstrap.php` を作成する。

```php
use Migrations\TestSuite\Migrator;

$findRoot = function($root) {
    do {
        $lastRoot = $root;
        $root = dirname($root);
        if (is_dir($root . '/vendor/cakephp/cakephp')) {
            return $root;
        }
    } while($root !== $lastRoot);
    throw new Exception("Cannot find the root of the application, unable to run tests");
};
$root = $findRoot(__FILE__);
unset($findRoot);
chdir($root);

require_once $root . '/vendor/autoload.php';
require $root . '/config/bootstrap.php';

(new Migrator())->runMany([
    ['plugin' => 'BaserCore'],
    ['plugin' => 'BcFavorite']
]);
```
ユニットテストの実行を composer 経由にする場合は、 `composer.json` に次を追記する。

```json
    "scripts": {
        "test": [
            "Composer\\Config::disableProcessTimeout",
            "../../vendor/bin/phpunit --colors=always"
        ]
    }
```

ただ、この場合、プラグインディレクトリ内で実行する事が前提となる。  
GitHubActionsなどのCIで、docker を使いつつ（[baserCMS５の開発の場合](https://github.com/baserproject/ucmitz)）自動化するためには、yml ファイルにて、 `--workdir` オプションを指定して次にように記述する必要がある。
（プラグインではなくアプリケーションルート側で設定する場合）

```yml
docker exec --workdir /var/www/html/plugins/bc-favorite [コンテナ名] composer run-script test
```

なお、特定ファイルを指定する場合のユニットテストのコマンドは次のようになる。

```shell
cd plugins/bc-favorite
../../vendor/bin/phpunit tests/TestCase/Controller/Api/FavoritesControllerTest.php 
```

- プラグインフォルダに移動しなければならない
- 上位階層の実行ファイルを指定しなければならない
- パス指定がコンテンツルートではなくプラグインルートからのパスとなる（最近のIDEでは、指定ファイルのコンテンツルートからのパスをコピーする機能があるがそれが利用できない）

めんどい。

　
### baserCMSコア、コアプラグイン開発の最適解

特定ファイルのパス指定が特にめんどいので、baserCMS５の開発では、ルート直下の `phpunit.xml.dist` にテスト対象のプラグインを全て定義し、ルート直下の `tests/bootstrap.php` に、対象プラグインのマイグレーションを全て記述することにした。

　
baserCMSの CakePHP４系化の開発プロジェクトのレポジトリはこちら。
https://github.com/baserproject/ucmitz
（協力者募集しています）