【PHPフレームワークFlow】FunctionalテストでDBアクセスを含めたテストを実行する

# 初めに
前回は[こちら](https://qiita.com/nnhkrnk/items/64869c0e6283c285e847)の記事でFunctionalテストについて紹介しました。今回はDBアクセスのあるAPIをFunctionalテストで実装していこうと思います。

# functionalテストとは
Functionalテストとは機能単位のテストを行うための機能です。Laravelのfeatureテストのようなイメージだと思います。

詳しくは前回の記事をご参照ください。

https://qiita.com/nnhkrnk/items/64869c0e6283c285e847

# DBアクセスを含めたFunctionalテストを書いてみる
前回の記事では固定値を返すだけの単純なAPIに対してFunctionalテストを作成しました。今回はDBアクセスを含めたControllerに対するFunctionalテストを作成してみようと思います。

こちらがテスト対象のクラスです。

```php: テスト対象のクラス
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Welcome\Domain\Model\Functional;

class FunctionalTestController extends ActionController
{
    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;

    /**
     * @Flow\Inject
     * @var \Neos\Welcome\Domain\Repository\FunctionalRepository
     */
    protected $functionalRepository;

    /**
     * @return void
     */
    public function indexAction()
    {
        // DBからデータを取得
        $result = $this->functionalRepository->findAll();

        // 最初の1件をレスポンスに詰める
        $first = $result->getFirst();
        $this->view->assign('value', [
            'id' => $first->getId(),
            'functionName' => $first->getFunctionalName()
        ]);
    }
}
```

## 1. テストケース作成
以下のようなテストケースを作成しました。
ポイントは後述します。

```php: テストクラス
<?php
namespace Neos\Welcome\Tests\Functional\Controller;

use Neos\Flow\Tests\FunctionalTestCase;
use Neos\Welcome\Domain\Model\Functional;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Neos\Welcome\Domain\Repository\FunctionalRepository;

class FunctionalTestControllerTest extends FunctionalTestCase
{

    /**
     * @var bool
     */
    protected $testableHttpEnabled = true;

    /**
     * ★ポイント1
     * @var boolean
     */
    protected static $testablePersistenceEnabled = true;

    /**
     * @var \Neos\Welcome\Domain\Repository\FunctionalRepository
     */
    protected $functionalRepository;

    /**
     * ★ポイント2
     * Additional setup
     */
    protected function setUp(): void
    {
        parent::setUp();
        $this->functionalRepository = $this->objectManager->get(FunctionalRepository::class);
    }

    /**
     * @test
     */
    public function functionaltest()
    {
        // Arrange
        // ★ポイント3
        $testData = new Functional("testId", "testName");
        $this->functionalRepository->add($testData);
        $this->persistenceManager->persistAll();

        // Act
        $response = $this->browser->request('http://localhost:8081/Neos.Welcome/FunctionalTest/index');
        $responseArray = json_decode($response->getBody()->getContents(), true);

        // Assert
        self::assertSame('testId', $responseArray['id']);
        self::assertSame('testName', $responseArray['functionName']);
        self::assertSame(200, $response->getStatusCode());
    }
}
```

### ポイント
* DB接続を行うテストでは`$testablePersistenceEnabled`を`true`にする

```php
    /**
     * @var boolean
     */
    protected static $testablePersistenceEnabled = true;
```


* データ作成や検証で利用するRepositoryは`setup()`の中で値を入れる
```php
    /**
     * @var \Neos\Welcome\Domain\Repository\FunctionalRepository
     */
    protected $functionalRepository;

    protected function setUp(): void
    {
        parent::setUp();
        $this->functionalRepository = $this->objectManager->get(FunctionalRepository::class);
    }
```

* テストケースの中でDBのデータを作成
  * `persistAll()`を行うことで永続化できる
```php
        // Arrange
        $testData = new Functional("testId", "testName");
        $this->functionalRepository->add($testData);
        $this->persistenceManager->persistAll();
```

## 2. Settings.yamlにDB接続設定を記載
設定ファイルにDB接続設定を記載します。テストはTestingコンテキストで実行されるため、ConfigtrationのTesting配下にSettings.yamlを作成し、そこに記載します。

```
Quickstart
    ├ Configuration/
        ├ Testing
        |    └ Settings.yaml(★)
        └ Settings.yaml

```

MySQLを使用してテストを行いたかったので、以下のように記載しました。
dbnameを`my_database_for_test`としていますが、このDBをあらかじめ作成しておく必要があります。
マイグレーションはFunctionalTest内で実行してくれるため必要ありません。`create database hoge`のみでOK。

```yaml
Neos:
  Flow:
    persistence:
      backendOptions:
        driver: 'pdo_mysql'
        charset: 'utf8mb4'
        host: '127.0.0.1'    # Docker service name
        dbname: 'my_database_for_test'  # Database name
        user: 'root'  # MySQL user
        password: 'root'  # MySQL password
        port: '3306'  # MySQL port
        driverOptions:
          1002: 'SET SESSION wait_timeout=5'
```

## 3. テスト実行
実行結果がこちらです。
テスト一つに対して4秒かかっているのは、マイグレーションやデータ作成/削除のせいですかね、複数テストがあった時にどうなるのかはまた今度検証しようと思います。

```sh
$ .\bin\phpunit -c .\Build\BuildEssentials\PhpUnit\FunctionalTests.xml .\Packages\Application\Neos.Welcome\Tests\Functional\Controller\FunctionalTestControllerTest.php --debug
PHPUnit 9.6.22 by Sebastian Bergmann and contributors.

Test 'Neos\Welcome\Tests\Functional\Controller\FunctionalTestControllerTest::functionaltest' started
Test 'Neos\Welcome\Tests\Functional\Controller\FunctionalTestControllerTest::functionaltest' ended


Time: 00:04.864, Memory: 92.00 MB

OK (1 test, 3 assertions)

```


# 終わりに
前回に引き続き、今回はFlowのFunctionalテストを試してみました。
DB接続のテストがサクッとつくれるのは開発が捗りますね。今回はControllerから実行していますが、FlowのFW自体のFunctionalテストでは、Repository自体にDBアクセスを含めたFunctionalテストを作成しており、UnitTestのような感覚で書けるのはいいなとおもいましたね。可能性を感じています。

ここまでご覧いただきありがとうございました！