CakePHP + PHPUnit でテスト共通の定数とメソッドを設置する

## 想定読者

* CakePHP + PHPUnit を利用していて改善ネタを探している方
* テストコードを次のように改善する状況に立ち会った方
  * 文字列などでハードコーディングされたテスト部分を、定数として抽出する
  * テストに特化したメソッドを共通化して再利用する(log など)

(他フレームワークについては未調査
 単に include_path をコネコネしているだけです)

## やり方

### {APP_ROOT}/app/Test の直下に ディレクトリ helper と ファイル test_helper.php を追加する

```php:{APP_ROOT}/app/Test/test_helper.php
1 <?php
2  ini_set('include_path', dirname(__FILE__) . PATH_SEPARATOR . ini_get('include_path'));
3  require_once('Test' . DS . 'helper' . DS . 'constants.php');
4  require_once('Test' . DS . 'helper' . DS . 'util.php');
5 ?>
```

* 2 行目で、test_helper.php から次のファイルを読込むようにした
  * 3 行目、テスト用定数ファイル「{APP_ROOT}/app/Test/helper/constants.php」
  * 4 行目、テスト用メソッドファイル「{APP_ROOT}/app/Test/helper/util.php」

### 適宜 {APP_ROOT}/app/Test/helper に定数ファイルとメソッドファイルを作成/編集する

今回は 定数 HELLO と メソッド  debug_log_in_test_code 次のように作成する

```php:{APP_ROOT}/app/Test/helper/constants.php
1 <?php
2   define('HELLO', 'hello world');
3 ?>
```

```php:{APP_ROOT}/app/Test/helper/util.php
1 <?php
2   /* echo out log for three times */
3   function debug_log_in_test_code($log_literal) {
4     for($i = 0; $i < 3; ++$i) {
5       echo $log_literal;
6       echo "\n";
7     }
8   }
9 ?>
```

---

ここまでで、テストに使用する定数とメソッドが「設置」できている

次に、各テストで設置したファイルを使用する

---

### 実際にテストで定数/メソッドを使用する

単体テストファイルが次の通りとする

```php:{APP_ROOT}/app/Test/Case/Controller/ExampleControllerTest.php
1  <?php
2   ini_set('include_path', dirname(dirname(dirname(__FILE__))) . PATH_SEPARATOR . ini_get('include_path'));
3   require_once('test_helper.php');
4
5   class ExampleControllerTest extends ControllerTestCase {
6     public function testGetIndex() {
7       $response = $this->testAction('/example', array('method' => 'get'));
8       debug_log_in_test_code($response);
9       $this->assertEquals($response, HELLO);
10    }
11  }
12 ?>
```

* 2, 3 行目を例に、test_helper.php を require しなければ、テスト用定数とメソッドが使えない
* 8 行目でテスト用メソッドを、9行目でテスト用定数を利用している

#### テスト対象のコントローラーが次の通りだとして

```php:{APP_ROOT}/app/Controller/ExampleController.php
1 <?php
2 App::uses('AppController', 'Controller');
3
4 class ExampleController extends AppController {
5   public function index() {
6     return 'hello world';
7   }
8 }
```

#### テストは問題なく実行される

```bash
[root@localhost app]# ./Console/cake test app Controller/ExampleController

Welcome to CakePHP v2.5.6 Console
---------------------------------------------------------------
App : app
Path: /opt/lampp/htdocs/cake/app/
---------------------------------------------------------------
CakePHP Test Shell
---------------------------------------------------------------
PHPUnit 3.7.32 by Sebastian Bergmann.
・
.hello world
hello world
hello world


Time: 138 ms, Memory: 11.25Mb
```

----

## テスト用定数の利用にあたって注意

プロダクション側に存在する定数名と同じ名前の定数をテスト側に書くと

```Constant {定義した定数名} already defined``` のエラーが発生、伴ってテストも失敗する

プロダクション側とテスト側の定数命名は何らかの規則で分けることが望ましい

以下、実例

---

プロダクション側で定数 HELLO を定義

```php:{APP_ROOT}/app/Controller/ExampleController.php
<?php

App::uses('AppController', 'Controller');
define('HELLO', 'world'); /* <<<< */

class ExampleController extends AppController {
  .
  .
```

テスト側でも定数 HELLO を定義

```php:{APP_ROOT}/app/Test/helper/constants.php
<?php
  define('HELLO', 'hello world'); /* <<<< */
?>
```

テストを実行すると、エラー発生により、テスト(前述で成功していたテスト)が失敗する

```php:bash
[root@localhost app]# ./Console/cake test app Controller/ExampleController

Welcome to CakePHP v2.5.6 Console
---------------------------------------------------------------
App : app
Path: /opt/lampp/htdocs/cake/app/
---------------------------------------------------------------
CakePHP Test Shell
---------------------------------------------------------------
PHPUnit 3.7.32 by Sebastian Bergmann.
E

Time: 129 ms, Memory: 9.75Mb

There was 1 error:

1) ExampleControllerTest::testGetIndex
Constant HELLO already defined

/opt/lampp/htdocs/example/app/Controller/ExampleController.php:4
/opt/lampp/htdocs/example/lib/Cake/Core/App.php:567
.
```

以上