CodeIgniter4のフィーチャーテストでデータベースを使う

この記事は CodeIgniter Advent Calendar 2020 - Qiita の15日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter4のフィーチャーテスト の続きです。

公式チュートリアルの news アプリ のフィーチャーテストを書いてみます。

このアプリはデータベースを使っているため、テスト用のデータベースの準備が必要になります。

テスト時のデータベース設定

PHPUnit でのテスト実行時には、CodeIgniter4 の環境は testing になります。

app/Config/Database.php

また、データベース設定ファイルの $tests が使われます。デフォルトでは、SQLite3 のメモリデータベースになっています。

このように、開発用、本番用データベースとテスト用のデータベースの種類を変えるためには、特定のデータベースに依存したコードが一切ないことが必要になります。まあ、あまり現実的ではないかも知れません。

app/Config/Database.php

...
    /**
     * This database connection is used when
     * running PHPUnit database tests.
     *
     * @var array
     */
    public $tests = [
        'DSN'      => '',
        'hostname' => '127.0.0.1',
        'username' => '',
        'password' => '',
        'database' => ':memory:',
        'DBDriver' => 'SQLite3',
        'DBPrefix' => 'db_',  // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
        'pConnect' => false,
        'DBDebug'  => (ENVIRONMENT !== 'production'),
        'cacheOn'  => false,
        'cacheDir' => '',
        'charset'  => 'utf8',
        'DBCollat' => 'utf8_general_ci',
        'swapPre'  => '',
        'encrypt'  => false,
        'compress' => false,
        'strictOn' => false,
        'failover' => [],
        'port'     => 3306,
    ];
...

デフォルトでは、DBPrefixdb_ になっています。

もし、CodeIgniter4 の Query Builder を経由せずに発行されるクエリがある場合は、開発用とテスト用のテーブル名を一致させるように DBPrefix を変更してください。

(2020-12-18 追記) 必要条件の記述をより正確にしました。

--- a/app/Config/Database.php
+++ b/app/Config/Database.php
@@ -64,7 +64,7 @@ class Database extends \CodeIgniter\Database\Config
        'password' => '',
        'database' => ':memory:',
        'DBDriver' => 'SQLite3',
-       'DBPrefix' => 'db_',  // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
+       'DBPrefix' => '',
        'pConnect' => false,
        'DBDebug'  => (ENVIRONMENT !== 'production'),
        'cacheOn'  => false,

phpunit.xml

また、phpunit.xml(作成されていない場合は phpunit.xml.dist)にて、PHPUnit 実行時の環境変数 database.tests.databasedatabase.tests.DBDriver が設定されています。

    <php>
        <!-- Directory containing phpunit.xml -->
        <const name="HOMEPATH" value="./"/>

        <!-- Directory containing the Paths config file -->
        <const name="CONFIGPATH" value="./app/Config/"/>

        <!-- Directory containing the front controller (index.php) -->
        <const name="PUBLICPATH" value="./public/"/>

        <!-- Database configuration -->
<!--    <env name="database.tests.hostname" value="localhost"/>  -->
<!--    <env name="database.tests.database" value="tests"/>      -->
<!--    <env name="database.tests.username" value="tests_user"/> -->
<!--    <env name="database.tests.password" value=""/>           -->
<!--    <env name="database.tests.DBDriver" value="MySQLi"/>     -->
<!--    <env name="database.tests.DBPrefix" value="tests_"/>     -->
        <env name="database.tests.database" value=":memory:"/>
        <env name="database.tests.DBDriver" value="SQLite3"/>
    </php>

CodeIgniter4 の設定は、設定ファイルよりも環境変数が優先(設定ファイルの値が環境変数の値で上書き)されます。

また、 phpunit 実行時には、.env より phpunit.xml が先に評価されますので、phpunit.xml が最優先されることになります。

テーブル作成とデータ挿入

とりあえず、以下で作ったマイグレーションとシーダーを使うことにします。

以下のようにテストファイルとプロパティを設定します。

tests/feature/NewsTest.php

<?php

namespace Tests\Feature;

use CodeIgniter\Test\FeatureTestCase;

class NewsTest extends FeatureTestCase
{
    protected $refresh = true;
    protected $namespace = 'App';

    protected $basePath = APPPATH . 'Database';
    protected $seed = 'NewsSeeder';
}

マイグレーション設定

$refresh$namespace はマイグレーションに関する設定です。

$refreshtrue にすると毎回データベースがリフレッシュされます。つまり、PHPUnit の setUp() メソッド実行時に、マイグレーション 0 まで戻して再度マイグレーションを実行します。

$namespace はマイグレーションファイルの名前空間です。Database\Migrations は含めません。指定しない場合は、tests/_support/Database/Migrations 以下を探します。

なお、個人的には、テスト実行時にデータベース構造が変わるわけではないので、毎回マイグレーションをやり直すのは無駄だと思っています。

シーダー設定

$basePath$seed はシーダーに関する設定です。

$basePath はシーダーファイルが配置されているパスを設定します。Seeds ディレクトリは含めません。指定しない場合は、tests/_support/Database/Seeds 以下を探します。

$seed は実行するシーダーファイル名を設定します。

実際には、開発用のシーダーとテスト用のシーダーは分けたほうがいいと思います。

テストコードの作成

テストコードを完成させましょう。

tests/feature/NewsTest.php

<?php

namespace Tests\Feature;

use CodeIgniter\Test\FeatureTestCase;

class NewsTest extends FeatureTestCase
{
    protected $refresh = true;
    protected $namespace = 'App';

    protected $basePath = APPPATH . 'Database';
    protected $seed = 'NewsSeeder';

    public function testGetNews()
    {
        $result = $this->get('news');

        $result->assertStatus(200);

        $result->assertSee('<title>CodeIgniter Tutorial</title>');
        $result->assertSee('Elvis was sighted at the Podunk internet cafe.');
    }
}

$this->get('news')$this->call('get', 'news') と同じ意味です。

データベースにシーダーのデータが挿入されれば、それが表示されるはずです。

テストの実行

phpunit を実行します。

$ vendor/bin/phpunit tests/feature/NewsTest.php
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

Error:         XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

.                                                                   1 / 1 (100%)

Time: 418 ms, Memory: 10.00 MB

OK (1 test, 3 assertions)

通りました。

CodeIgniter4のフィーチャーテストでモックを使う に続きます。

この記事は CodeIgniter Advent Calendar 2020 - Qiita の15日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Date: 2020/12/15

Tags: codeigniter, codeigniter4, testing, database