PHPUnit + DBUnit + YAML で MySQL をテストする


PHPUnit マニュアル – 第8章 データベースのテスト
https://phpunit.de/manual/current/ja/database.html

で紹介されている、YAMLデータセットを使ったテストを動かしてみる。データベースに MySQL を使う。

ソースコードはこちら：

takatama/php-dbunit-yaml
https://github.com/takatama/php-dbunit-yaml

Mac OS X を使って開発する。

```
$ sw_vers -productVersion 
10.10.2
```

## MySQL の準備

### MySQL をインストールする

Mac へ MySQL を Homebrew でインストールする手順 - Qiita
http://qiita.com/hkusu/items/cda3e8461e7a46ecf25d

### ユーザー、データベース、パスワードを作る

ユーザー user@localhost を、パスワード passwd で作る。

データベース myguestbook に、テーブル guestbook を作る。

```create_guestbook.sql
grant all on myguestbook.* to 'user'@'localhost' identified by 'passwd';
create database if not exists myguestbook;
create table if not exists myguestbook.guestbook (id int auto_increment, content varchar(255), user varchar(255), created timestamp, index(id));
```

```
$ mysql -u root -p < create_guestbook.sql
```

正しくできているか確認する。

```
$ mysql -uuser -ppasswd myguestbook

Warning: Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 5.6.23 Homebrew

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
+------------------------------+
| Tables_in_guestbook_database |
+------------------------------+
| guestbook                    |
+------------------------------+
1 row in set (0.00 sec)

mysql> desc guestbook;
+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | int(11)      | NO   | MUL | NULL    | auto_increment |
| content | varchar(255) | YES  |     | NULL    |                |
| user    | varchar(255) | YES  |     | NULL    |                |
| created | datetime     | YES  |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
```

## PHP の準備

### PHP をインストールする

Homebrew を使って PHP5.6 をインストールする。

Homebrew/homebrew-php
https://github.com/Homebrew/homebrew-php

```
$ brew tap homebrew/dupes
$ brew tap homebrew/versions
$ brew tab homebrew/homebrew-php
```

どのオプションを付けるか選ぶ。

```
$ brew options php56
```

今回は素の PHP5.6 をインストールする。

```
$ brew install php56
```

正しくインストールされたか確認する。

```
$ php -v
PHP 5.6.6 (cli) (built: Feb 26 2015 12:42:39) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies
    with Xdebug v2.3.1, Copyright (c) 2002-2015, by Derick Rethans
```
    
### Composer をインストールする

composer がインストールできるか確認する。

```
$ brew search composer
composer
```

composer をインストールする。

```
$brew install composer
```

正しくインストールされていることを確認する。

```
$ composer -V
Composer version 1.0.0-alpha9 2014-12-07 17:15:20
```

### PHPUnit, DBUnit をインストールする
composer.json の require-dev (開発環境にだけインストールするパッケージ) に phpunit と dbunit を指定する。

```composer.json
"require-dev": {
    "phpunit/phpunit": "4.5.*",
    "phpunit/dbunit": ">=1.2"
}
```

パッケージをインストールする。

```
$ composer update
```

### PHPUnit マニュアルを読む

PHPUnit マニュアル – 第8章 データベースのテスト
https://phpunit.de/manual/current/ja/database.html

今回は YAML 形式で Fixute を準備するやり方を採用する。

### PHPUnit の設定情報にデータベースへの接続情報を書く

phpunit.xml にデータベースへに接続に必要な情報を書く。PHPUnit を実行するときに引数でこのファイルを指定する。

```phpunit.xml
<?xml version="1.0" encoding="UTF-8" ?>
<phpunit>
    <php>
        <var name="DB_DSN" value="mysql:dbname=myguestbook;host=localhost" />
        <var name="DB_USER" value="user" />
        <var name="DB_PASSWD" value="passwd" />
        <var name="DB_DBNAME" value="myguestbook" />
    </php>
</phpunit>
```

### Generic_Tests_DatabaseTestCase.php を作る

```Generic_Tests_DatabaseTestCase.php
<?php
abstract class Generic_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
    // PDO のインスタンス生成は、クリーンアップおよびフィクスチャ読み込みのときに一度だけ
    static private $pdo = null;

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

    /**
     * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    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;
    }
}
```

### Fixute を作る

データを2つ準備する。

```
$ mkdir _files
$ vi guestbook.yml
```

```guestbook.yml
guestbook:
  -
    id: 1
    content: "Hello buddy!"
    user: "joe"
    created: "2010-04-24 17:15:23"
  -
    id: 2
    content: "I like it!"
    user:
    created: "2010-04-26 12:14:20"
```

### YAML データセットを使うテストケースを作る
2件のデータが登録されていることを確認する。

```YamlGuestbookTest.php
<?php

require_once dirname(__FILE__).'/Generic_Tests_DatabaseTestCase.php';

class YamlGuestbookTest extends Generic_Tests_DatabaseTestCase
{
    /**
     * @return PHPUnit_Extensions_Database_DataSet_IDataSet
     */
    protected function getDataSet()
    {
        return new PHPUnit_Extensions_Database_DataSet_YamlDataSet(
            dirname(__FILE__)."/_files/guestbook.yml"
        );
    }

    public function testGetRowCount()
    {
        $this->assertEquals(2, $this->getConnection()->getRowCount('guestbook'));
    }
}
```

引数に設定ファイルを指定して実行する。

```
$ vendor/bin/phpunit -c phpunit.xml YamlGuestbookTest.php 
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

Configuration read from /Users/takatama/openshift/ranking/phpunit.xml

.

Time: 125 ms, Memory: 4.25Mb

OK (1 test, 1 assertion)
```

### よくある間違い

データベースに guestbook テーブルがないのにテストを実行しようとすると、以下のエラーが出る。

```
$ vendor/bin/phpunit -c phpunit.xml YamlGuestbookTest.php 
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

E

Time: 119 ms, Memory: 3.75Mb

There was 1 error:

1) YamlGuestbookTest::testGetRowCount
PHPUnit_Extensions_Database_Operation_Exception: COMPOSITE[TRUNCATE] operation failed on query: 
                DELETE FROM "guestbook"
             using args: Array
(
)
 [SQLSTATE[HY000]: General error: 1 no such table: guestbook]

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
```

参考: 
database - Loading schema from SQL file, in PHPUnit test - Stack Overflow
http://stackoverflow.com/questions/10948273/loading-schema-from-sql-file-in-phpunit-test