【PHP】PHPフレームワーク「Flow」を試してみる～FunctionalTest編～

# 初めに
FlowにはFunctionalTestという機能があり、PHPUnitを用いてAPIやDBなどの機能をテストすることができます。今回はこのFunctionalTestを試してみます。

# 実行方法
今回はControllerを対象にテストを行います。
テスト対象は以下です。単純にレスポンスを返すだけのシンプルなAPI。
```php: テスト対象のControllerクラス
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;

class FunctionalTestController extends ActionController
{

    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;

    /**
     * @return void
     */
    public function indexAction()
    {
        $this->view->assign('value', array('testResponse'));
    }
}

```

## 1. テストクラスを作成
以下のようなテストクラスを作成しました。
FunctionalTestをするには、Flowの`Neos\Flow\Tests\FunctionalTestCase`というクラスを継承する必要があります。

```php
<?php
namespace Neos\Welcome\Tests\Functional\Controller;

use Neos\Flow\Tests\FunctionalTestCase;
use Psr\Http\Message\ServerRequestFactoryInterface;

class FunctionalTestControllerTest extends FunctionalTestCase
{

    /**
     * @var bool
     */
    protected $testableHttpEnabled = true; // ★APIのリクエストを送るためのフラグ

    /**
     * @var ServerRequestFactoryInterface
     */
    protected $serverRequestFactory;

    /**
     * Additional setup: Routes
     */
    protected function setUp(): void
    {
        parent::setUp();

        // ★APIのルーティング設定
        $this->registerRoute('test', 'Neos.Welcome/Aop/index(/{@action})', [
            '@package' => 'Neos.Welcome',
            '@controller' => 'FunctionalTest',
            '@action' => 'index',
        ]);

        $this->serverRequestFactory = $this->objectManager->get(ServerRequestFactoryInterface::class);
    }

    /**
     * @test
     */
    public function functionaltest()
    {
        // ★実行と検証
        $response = $this->browser->request('http://localhost:8081/Neos.Welcome/Aop/index');
        self::assertSame(200, $response->getStatusCode());
    }
}

```

## 2. テスト実行
以下のコマンドでテストを実行します。
設定ファイルで定義している`Build\BuildEssentials\PhpUnit\FunctionalTests.xml `はFlowにデフォルトで搭載されているPHPUnitの設定ファイルです。

```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:00.093, Memory: 84.00 MB

OK (1 test, 1 assertion)
```

無事にテストが実行されました！

# 注意点

実行した際、以下のエラーが出て実行をすることができませんでした。

```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:00.075, Memory: 82.00 MB

There was 1 error:

1) Neos\Welcome\Tests\Functional\Controller\FunctionalTestControllerTest::functionaltest
TypeError: Attribute value must be of type int for selected attribute, string given

C:\Path\To\Project\Packages\Libraries\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnection.php:40
C:\Path\To\Project\Packages\Libraries\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOSqlite\Driver.php:42
C:\Path\To\Project\Packages\Libraries\doctrine\dbal\lib\Doctrine\DBAL\Connection.php:412
C:\Path\To\Project\Data\Temporary\Testing\Cache\Code\Flow_Object_Classes\Neos_Flow_Persistence_Doctrine_EntityManagerFactory.php:111
C:\Path\To\Project\Packages\Framework\Neos.Flow\Classes\ObjectManagement\ObjectManager.php:517
C:\Path\To\Project\Packages\Framework\Neos.Flow\Classes\ObjectManagement\ObjectManager.php:206
C:\Path\To\Project\Data\Temporary\Testing\Cache\Code\Flow_Object_Classes\Neos_Flow_Persistence_Doctrine_PersistenceManager.php:536
C:\Path\To\Project\Packages\Framework\Neos.Flow\Classes\ObjectManagement\DependencyInjection\DependencyProxy.php:59
C:\Path\To\Project\Packages\Framework\Neos.Flow\Classes\ObjectManagement\DependencyInjection\DependencyProxy.php:59
C:\Path\To\Project\Packages\Framework\Neos.Flow\Classes\ObjectManagement\DependencyInjection\DependencyProxy.php:99
C:\Path\To\Project\Data\Temporary\Testing\Cache\Code\Flow_Object_Classes\Neos_Flow_Persistence_Doctrine_PersistenceManager.php:142
C:\Path\To\Project\Packages\Framework\Neos.Flow\Classes\ObjectManagement\DependencyInjection\DependencyProxy.php:99
C:\Path\To\Project\Data\Temporary\Testing\Cache\Code\Flow_Object_Classes\Neos_Flow_Http_Client_InternalRequestEngine.php:162
C:\Path\To\Project\Data\Temporary\Testing\Cache\Code\Flow_Object_Classes\Neos_Flow_Http_Client_Browser.php:219
C:\Path\To\Project\Data\Temporary\Testing\Cache\Code\Flow_Object_Classes\Neos_Flow_Http_Client_Browser.php:172
C:\Path\To\Project\DistributionPackages\Neos.Welcome\Tests\Functional\Controller\FunctionalTestControllerTest.php:41
C:\Path\To\Project\bin\phpunit:122

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
```
FunctionalTestはデフォルトでSQLiteを用いる設定になっているのですが、そのままだとDoctrineのPDOConnectionで渡す引数の型が違うというエラーがでてしまいました。

理由はわからないのですが、原因の箇所のコードを以下のように修正すると通るようになりました。
今回は試すだけなのでこれでいいですが、理由は調べようと思います。

```diff_php: Doctrine\DBAL\Driver\PDOConnection
    public function __construct($dsn, $user = null, $password = null, ?array $options = null)
    {
        try {
-           parent::__construct($dsn, (string) $user, (string) $password, (array) $options);
+           parent::__construct($dsn, (string) $user, (string) $password);
            $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]);
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $exception) {
            throw Exception::new($exception);
        }
    }
```

# 終わりに
今回FlowのFunctionalTestを使用してAPIのテストを実施しました。今回は単純なAPIでしたが、DBまでの一気通貫のテストもできそうなので、別の記事で試してみようと思います。

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