footmark

ひよっこエンジニアの足跡

【PHPUnit x MySQL】データベースのテスト

PHPでデータベースのテストをするためのメモ。

データベーステストの四段階

データベーステストは次の四段階を踏む必要があります。

Gerard Meszaros は、著書 xUnit Test Patterns でユニットテストを次の四段階に分類しています。
1. フィクスチャのセットアップ (Setup)
2. テストしたいシステムの実行 (Exercise)
3. 結果の検証 (Verify)
4. 後始末 (Teardown

PHPUnit マニュアル - 第8章 データベースのテスト

このテストの仕組みを一から作るのは面倒です。
そこで、PHPUnitDBUnit拡張を使います。

DBUnit拡張を使うことによって、データベースの初期化やテストに使用したデータの後始末をうまくやってくれます。

データベース設定を記述

テストに使うデータベースを設定します。
phpunit.xml にデータベース接続に必要な情報を記述します。

phpunit.xml
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit
    bootstrap="src/autoload.php"
    colors="true">
    <testsuite name="tddbc4">
        <directory>tests</directory>
    </testsuite>
    <php>
        <var name="DB_DSN" value="mysql:dbname=tddbc4;host=localhost" />
        <var name="DB_USER" value="user" />
        <var name="DB_PASSWD" value="password" />
        <var name="DB_DBNAME" value="tddbc4" />
    </php>
</phpunit>

テーブルについて

データベーステストで用いるテーブルは予め用意しておく必要があります。
今回用意したテーブルはこんな感じです。

f:id:yuk4420:20141126173906p:plain

(ER図の関連線の引き方よく分かってない)

ちなみに TDDBC お題 のお題4を通して勉強したのでそのようなテーブルになってます。

初期化に用いるデータの準備

次に初期化に用いるデータセットを用意します。
XMLCSV、Array等、様々な形式でデータセットを用意することができます。

今回はMySQL XMLデータセットを使いました。
データベースにテストデータとするレコードが入っている状態で、mysqldump コマンドの --xml オプションでdumpすることで簡単作成できます。

tests/data_set.xml
<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<database name="tddbc4">
    <table_data name="ng_word">
    <row>
        <field name="id">1</field>
        <field name="name">Arsenal</field>
    </row>
    <row>
        <field name="id">2</field>
        <field name="name">悟空</field>
    </row>
    </table_data>
    <table_data name="user">
    <row>
        <field name="id">2</field>
        <field name="name">taro</field>
    </row>
    <row>
        <field name="id">1</field>
        <field name="name">t_wada</field>
    </row>
    </table_data>
    <table_data name="user_ng_word">
    <row>
        <field name="id">1</field>
        <field name="user_id">1</field>
        <field name="ng_word_id">1</field>
    </row>
    <row>
        <field name="id">2</field>
        <field name="user_id">1</field>
        <field name="ng_word_id">2</field>
    </row>
    <row>
        <field name="id">3</field>
        <field name="user_id">2</field>
        <field name="ng_word_id">2</field>
    </row>
    </table_data>
</database>
</mysqldump>

テストコードの記述

データベーステストを行うためにまず、PHPUnit_Extensions_Database_TestCase 抽象テストケースを継承したクラス(今回の場合、Generic_Tests_DatabaseTestCase クラス)を作ります。
さらに、2つの抽象メソッド getConnection()getDataSet() を実装します。
これらのメソッドはデータベース接続と初期化が目的です。

実際のテストコードは Generic_Tests_DatabaseTestCase クラスを継承したクラスに記述します。

tests/DatabaseTest.php
<?php

// =====
// 引用元
// =====
// PHPUnit マニュアル - 第8章 データベースのテスト
// https://phpunit.de/manual/current/ja/database.html
abstract class Generic_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
    // PDO のインスタンス生成は、クリーンアップおよびフィクスチャ読み込みのときに一度だけ
    static private $pdo = null;

    // PHPUnit_Extensions_Database_DB_IDatabaseConnection のインスタンス生成は、テストごとに一度だけ
    private $conn = null;

    final public function getConnection()
    {
        if ($this->conn === null) {
            if (self::$pdo == null) {
                self::$pdo = new PDO( $GLOBALS['DB_DSN'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD'] );
            }
            $this->conn = $this->createDefaultDBConnection(self::$pdo, $GLOBALS['DB_DBNAME']);
        }

        return $this->conn;
    }

    public function getDataSet()
    {
        return $this->createMySQLXMLDataSet('tests/data_set.xml');
    }
}

class DatabaseTest extends Generic_Tests_DatabaseTestCase 
{
    /**
     * @test
     */
    public function 指定したng_wordが検閲に引っ掛かった回数を表示できること()
    {
        $filter = new WordFilter();

        $ng_word = '悟空';
        $num_of_ng_word = $filter->get_num_of_ng_word($ng_word);

        $this->assertEquals(2, $num_of_ng_word);
    }
}

あとはREDとGREENを回すだけです。

実際にデータベーステストを行うと、面倒なことを凄く簡単にできている感があります。

参考