単純な関数に関してのユニットテストは簡単ですが、DBを伴うユニットテストについては自動化が面倒な状況にありました。RubyOnRails等で使われている、fixturesの考えに基づいて、以下のような方法で自動テストを行うことができるようになったので記録しておきます。
概要
テスト用に用意したDBに対して、CSVで作成したテスト用のデータをimportしながら、各クラスをテストしておく。CSVで予めテストデータを作成しておくため、テストコードの中に予め「メールアドレスが〜であるもの」という条件でテストコードを書くことができる。
前提条件
- PHPのCLI版で、PDO, pgsqlが利用出来ること
- テスト用のデータベースを用意しておくこと
- RoRでは、production(本番)、development(開発)、test(テスト)を用意することになっている
下記のオプションでCLI版のPHPをインストールしておきます。(その他のオプションも必要かもしれませんが未確認)
$./configure --with-curl --with-pgsql=/usr/local/pgsql --with-pdo-pgsql
テストコード
テストされるクラス
テストされるクラスは以下のようなものとします。
PHPUnitの命名規約に従うよう、Member.phpというファイルにはMember というクラスにしておく必要があります。
<?php require_once('Model.php'); // Sample class class Member extends Model { /** * database */ protected $dbh; public function __construct($dbh) { $this->dbh = $dbh; } public function getMemberById($id) { $sql = "SELECT * FROM members WHERE id = ? "; $bind_values = array($id); return $this->getOne($sql, $bind_values); } } ?>
テストコード
テストクラスを記述します。MemberTest.php という名前で記述します。(これも命名規約によるもの)
<?php // E_DEPRECATD が ADODB の中の split で発生してしまうので、とりあえず... error_reporting(E_ERROR); require_once 'PHPUnit/Extensions/Database/TestCase.php'; require_once 'PHPUnit/Extensions/Database/DataSet/CsvDataSet.php'; // 必要なライブラリだけを読み込むようにしておく require_once 'lib/Member.php'; require_once 'lib/Db.php'; class MemberTest extends PHPUnit_Extensions_Database_TestCase { protected $object; protected $dbh; /** * 初期化処理。 * テスト開始前にDBへの接続と、対象クラスの初期化を行っておく */ protected function setUp() { $dbh = Db::Db_Connect(); $this->object = new Member($dbh); $this->dbh = $dbh; } /** * PDO による接続 */ protected function getConnection() { $pdo = new PDO('pgsql:host=localhost;dbname=mydatabase_test', 'username', 'password'); return $this->createDefaultDBConnection($pdo, 'mydatabase_test'); } /** * fixturesに配置したCSVからデータを読み取りDBに入れる */ protected function getDataSet() { $dataSet = new PHPUnit_Extensions_Database_DataSet_CsvDataSet(); // membersテーブルにcsvをインポートするための記述 $dataSet->addTable('members', 'fixtures/member.csv'); return $dataSet; } /** * 終了処理 * DBの切断と解放処理 */ protected function tearDown() { $this->object = NULL; $this->dbh->close(); } public function testInitDataSet() { // ここに書いてる通りにやると失敗する... 未調査 // http://thinkit.co.jp/article/1197/1 // // 無理やり成功させておく... $this->assertEquals(TRUE, TRUE); } /** * Member::getMemberById のテスト */ public function testGetMemberById() { // 存在しない会員 $this->assertEquals(NULL, $this->object->getMemberById(0)); // 会員番号1(fixturesで読み込ませたもの) $member1 = $this->object->getMemberById(1); $this->assertEquals($member1['id'], 1); $this->assertEquals($member1['email'], 'kidd-number5@example.jp'); $this->assertEquals($member1['nickname'], 'tetsu'); // 会員番号2 ... (略) } } ?>
テストデータ
fixturesフォルダの中に、 member.csv を下記のように作成します。
id,email,nickname 1,kidd-number5@example.jp,tetsu 2,aaaa@example.jp,aaaa 3,bbbb@example.jp,bbbb
最初の行はDBのカラムに対応するように、次のカラムからは実際のデータを入れていきます。
テストの実行
実行します
$ phpunit MemberTest PHPUnit 3.5.11 by Sebastian Bergmann. .. Time: 0 seconds, Memory: 4.25Mb OK (2 tests, 5 assertions)
失敗するテストを書いておくと下記のようになります。
PHPUnit 3.5.11 by Sebastian Bergmann. .F Time: 1 second, Memory: 4.25Mb There was 1 failure: 1) MemberTest::testGetMemberById Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -kidd-number5@example.jp +xxxx@example.jp lib/MemberTest.php:81 FAILURES! Tests: 2, Assertions: 4, Failures: 1.