【cakephp2】bakeを拡張してTest用のFixtureをいい感じに生成する

ユニットテスト用のFixtureをいちいち作るのは面倒なのでDBから生成できるようにする。

環境

CakePHP2.7.x

要件

  1. schemaは自動生成
  2. データも自動生成
  3. データは新しいものから最新100件
  4. 生成したFixtureはサブディレクトリに配置
  5. 以上を1コマンドで行う

上記を達成するため、bakeコマンドを拡張する。

※注意 サブディレクトリ中のフィクスチャを読み込めるのはCakePHP 2.5.0以降のみなので
それ以前の方は諦めるかいい感じにimportしてください。

準備

1, 2, は初期bakeコマンドで可能。
3の新しいものから生成、4のサブディレクトリへの配置ができるように
bakeコマンドを拡張する。

1, app/LibへFixtureTask.phpを複製、override用のファイルをコピーする。

Fixtureのbake時の挙動は lib/Cake/Console/Command/Task/FixtureTask.php で定義されている。
これを app/Lib/Cake/Console/Command/Task/FixtureTask.php にコピーしてoverride用のファイルを作る。

※libの中のファイルはapp/Lib以下に同パスを作っておけばoverrideできる。
※なおapp/Console/Command/Task/FixtureTask.php においてもoverrideできる。Cakeは複数の対応するパスを順々に読んで存在したら返す、というルーティングをよくやる。

2, コピーしたoverride用のファイルを編集し、サブディレクトリ設定を受け取れるようにする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// @l95あたり

))->addOption('records', array(
'help' => __d('cake_console', 'Used with --count and <name>/all commands to pull [n] records from the live tables, ' .
'where [n] is either --count or the default of 10.'),
'short' => 'r',
'boolean' => true
/*** ここから追加 ***/
))->addOption('directory', array(
'help' => __d('cake_console', 'Set sub-directory in Test/Fixture/ for generated fixtures.'),
'short' => 'd',
/*** ここまで追加 ***/
))->epilog(
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
);

return $parser;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// l300あたり

/**
* Get the path to the fixtures.
*
* @return string Path for the fixtures
*/
public function getPath() {
$path = $this->path;
if (isset($this->plugin)) {
$path = $this->_pluginPath($this->plugin) . 'Test' . DS . 'Fixture' . DS;
}

/*** ここから追加 ***/
if(isset($this->params["directory"])){
$path .= $this->params["directory"]. DS;
}
/*** ここまで追加 ***/

return $path;
}

これでbakeコマンドに -d [subdirectory-name] のオプションが増え
Text/Fixture/[subdirectory-name]/に生成できるようになる。

3, コピーしたoverride用のファイルを編集し、新しいデータから順に取得できるようにする。

1
2
3
4
5
6
7
8
9
10
// l425あたり
protected function _getRecordsFromTable($modelName, $useTable = null) {

/** 20 lines or so *** /

$records = $modelObject->find('all', array(
'conditions' => $condition,
'order'=>'id desc', // <= これを追加
'limit' => $recordCount
));

※idのfieldがないテーブルだと使えないけど、CakePHP2の規約に沿っていればそんなことないはず
※2のようにオプション拡張しても良い

コマンドを叩く

1
app/Console/cake bake fixture -r -f -n 100 -c default -d base all

これで全テーブルの最新100件のデータを含んだfixtureがTest/Fixture/base以下に生成されます。
実際はこれを叩きにして個々のテスト用のFixtureを作るべきかな

読みこむ

1
2
3
class ArticleTest extends CakeTestCase {
public $fixtures = array('app.base/article', 'app.base/comment');
}

app.[subdirectory]/[fixture]とする。Test/Fixture直下においた場合app.をつけずに読み込めた場合でも
サブディレクトリに置くときはappから始めないと読み込まない。(はまった)

おまけ

bake fixtureで Fixtureを生成するのはいいとして一応bakeオプションの説明。
和訳しただけです。

オプション 説明
–help, -h ヘルプの表示
–verbose, -v bake中に細かく出力する
–quiet, -q bake中に出力しない
–count, -n DBから生成したデータを使うときにFixture内に含むデータの数 (初期値: 1)
–connection, -c bakeで使う参照元データベース(初期値: default)
–plugin, -p Fixtureをbakeするときにつかうプラグインの名前(キャメルケース)
–schema, -s Fixtureの中にスキーマをハードコーディングせずに毎回テーブルから取得する。
–theme, -t コードをbakeするときのテーマ。統一テーマを決められる。
–force, -f すでにFixtureファイルがあってもyes/noを訊かずに上書きする
–records, -r –countと<テーブル名>/all と一緒につかうと–countで指定した数のレコードを生きているテーブルから取得する。–countで指定しない場合初期値は10件。
–directory, -d 追加したもの 生成したFixtureを置く Test/Fixture/ 以下のサブディレクトリを設定する

参考

CakePHP 2.x Cookbook - テスト
https://book.cakephp.org/2/ja/development/testing.html

Qiita - CakePHP2でBakeでFixtureをディレクトリ分けする方法
https://qiita.com/yyanbow/items/f8aa8dbc4240e3180cb5