この記事は、Zennに投稿した以下の記事の再投稿になります。
はじめに
タイトルの通りですが、インメモリのデータベースを使ってテストを実行します。
最初、ググった通りにやっても上手くいかなくて苦戦したので、備忘録がてら手順をまとめていきます。
対象者
- EC CUBEのPHPUnitテストを、インメモリのデータベースを使って実行したい方
- 一部、EC CUBE固有のファイルが出てきますが、Symfonyを使っていれば大体同じ手順でできると思います
動作環境・前提
バージョン | |
---|---|
Mac | 12.x |
PHP | 7.4.33 |
SQLite | 3.37.0 |
Symfony | 5.4.21 |
EC CUBE | 4.x |
早速実装していく
doctrine.yaml
まず、今回最も重要になるdoctrine.yamlファイルを編集していきます。
ここでは、ドライバーやデータベースのURLなどの設定を行います。
ここが間違っていたら何もかも上手くいかないです。
doctrine:
dbal:
connections:
default:
driver: 'pdo_sqlite'
charset: utf8
url: 'sqlite:///:memory:'
dbname: 'test_db'
細かく話すとめちゃくちゃ長くなるのでざっくり説明すると、driver: 'pdo_sqlite'
でSQLiteを使うこと、url: 'sqlite:///:memory:'
でインメモリのデータベースを使うことを指定しています。
詳しくは公式サイトを見ていただくのがいいと思いますので、以下を参考にしてください。
phpunit.xml
続いて、phpunit.xmlを編集します。
編集するのは以下の部分です。
<!-- 省略 -->
<php>
<ini name="error_reporting" value="-1" />
<env name="KERNEL_CLASS" value="Eccube\Kernel" />
<env name="APP_ENV" value="test" force="true"/>
<env name="APP_DEBUG" value="1" />
<env name="SHELL_VERBOSITY" value="-1" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
<!-- 以下を追加 -->
<env name="DATABASE_URL" value="sqlite:///:memory:"/>
<!-- define your env variables for the test env here -->
</php>
<!-- 省略 -->
ここでもdoctrine.yaml
と同じくDBのURLを記述しておく必要があります。
他はデフォルトのままでも大丈夫だと思います。
また、上記の例でenv
としている部分が、server
となっている場合もあるようです。
こちらは、ご自身の環境に合わせて書き換えてください。
ここまでできたら、テストでインメモリのDBを使うことができるようになっています。
DBを使うテストが実装済みの方は、このタイミングで一度実行してみてください。
...実行できたでしょうか?
おそらくこの時点だとエラーまみれになりますが、それで正常です。
(逆にエラーにならなかった場合、もしかしたらここまでの手順にミスがあるかもしれません。)
EccubeTestCase.php
先ほどテストを実行した際、エラーメッセージのほとんどが「DBがありません」とか「テーブルがありません」みたいな内容だったのではないでしょうか?
それもそのはず。まだデータベースを作成するコードを書いていませんからね。
ということで、テストの前にデータベースを作成し、スキーマを更新するコードを書いていきます。
ここで注意してほしいのは、ターミナルでデータベースを作成するコマンドを実行しても無駄、ということです。
なぜなら、インメモリのデータベースは、プロセスが終了すると削除されてしまうからです。
$ symfony console doctrine:database:create --env=test
# ↓この時点でプロセスは終了している(DBが作られた次の瞬間、そのDBが削除される)
Created database "test_eccubedb" for connection named default
$ vendor/bin/phpunit --testsuite TestSuite
# 新しいプロセスになるので、エラーになる
EEEEEE
ではどうするのかというと、テストのコードを実行する直前(=同じプロセスの中)で、テスト用のデータベースを初期化するコードをPHPで書くことにします。
以下は、EccubeTestCase.php
(名前の通りですが、こちらがEC CUBE固有のファイルです)の一部です。
データベースを扱うテスト全てにこのクラスを継承させることを前提として、setUp()
メソッドの中でデータベースを初期化するコードを追記していきます。
先述した2つのファイルでインメモリのデータベースを使うように設定してあるので、ここではその辺りの設定をする必要はありません。勝手にテスト用のデータベースを使ってくれます。
では、まずデータベースを初期化するメソッドを実装します。
こんな感じ。
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Bundle\FrameworkBundle\Console\Application;
private function initDatabase(KernelInterface $kernel): void
{
// スキーマの更新
$entityManager = $kernel->getContainer()->get('doctrine.orm.entity_manager');
$metaData = $entityManager->getMetadataFactory()->getAllMetadata();
$schemaTool = new SchemaTool($entityManager);
$schemaTool->updateSchema($metaData);
$application = new Application($kernel);
$application->setAutoExit(false);
// フィクスチャーをロード
$input = new ArrayInput(['command' => 'eccube:fixtures:load']);
$output = new BufferedOutput();
$application->run($input, $output);
}
Symfony、Doctrineの標準機能を使ってデータベースの初期化を行っています。
それぞれの機能については、公式ドキュメントを読むのが早いと思うので、詳しく知りたい方は以下のサイトを参考にしてください。
公式ドキュメントではないですが、以下のサイトも参考になったので紹介しておきます。
「詳細はいいからとりあえず動くとこまでやりたい」という方は、一旦このまま先に進んでもらって大丈夫です。
このメソッドを、setUp()
メソッドで呼び出します。
public function setUp()
{
parent::setUp();
$this->client = static::createClient();
$kernel = self::bootKernel();
// ここで呼び出す
$this->initDatabase($kernel);
$this->entityManager = self::$container->get('doctrine')->getManager();
$this->eccubeConfig = self::$container->get(EccubeConfig::class);
}
これでそれぞれのテストが実行される直前にデータベースが作れられるようになりました。
早速、このクラスを継承したテストを実装していきましょう。
use Eccube\Tests\EccubeTestCase;
class DoctrineTest extends EccubeTestCase
{
public function setUp()
{
parent::setUp();
}
public function test(): void
{
// 任意のテストコード
}
}
任意のテストコード
の部分には、データベースとのやり取りを含むコードを実装してみてください(データベース使わないテストだと上手くいっているか判断できないので)。
では、再度ターミナルなどを起動して、このテストを実行してみてください。
今度は「DBがありません」や「テーブルがありません」のようなエラーは出なくなっているはずです。
もしエラーが出てしまった方は、doctrine.yaml
にミスがないか、initDatabase()
の実装にミスがないか、setUp()
メソッドで呼び出せているか、継承するクラスを間違えていないか等々、確認してみてください。
テストが無事にパスしたら成功です!
おまけ
ちなみに、このテストはGithubActionsでもちゃんとパスします。
以下は、私が実際に書いたGithubActionsのコードの一部です。
# 省略
steps:
- name: Setup PHP 7.4.3
uses: shivammathur/setup-php@master
with:
php-version: '7.4.3'
tools: phpunit
- name: Checkout
uses: actions/checkout@master
- name: Composer cache clear
id: composer-cache
run: |
echo "::set-output name=dir::$(composer config cache-files-dir)"
- uses: actions/cache@v1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Composer Install
run: composer update --dev --no-interaction -o --apcu-autoloader
- name: Run test suite
run: vendor/bin/phpunit --testsuite TestSuite
ブログ用に簡略化して書きましたが、こんな感じのGAを使っています。