cakephp2.2 + PHPUnit + postgres でsequenceがうまく動かない場合の対処方法
ちょっとハマったのでメモ
発端は
一ファイル毎のテストは成功するのに、testsuite作って同時に実行するとduplicate keyでエラーになる場合がある!
調べてみると、fixtureでinsert()するときにsequenceがリセットされてないっぽい
なんでじゃー。しかも成功するときもある。2回に1回ぐらいの割合で繰り返す。不思議フシギー
試した事
fixtureのid(sequenceカラム)を消す
ここに書いたけど、fixtureのときにid指定しないと、auto incrementが効いて、insert()でもこけない。
これで解決か?と思われたが、、、当然、他のテーブルでこのidでjoinなんかしてると、結合できない場合が発生して、あうと。それにこの方法は、テスト時の登録データのidが固定されないので、あまりよろしくない気もする。
setUp()で無理矢理sequenceを合わせてみる
テストの前処理でなんとかならんかと考えて、やってみた。
public function setUp() {
parent::setUp();
$this->User = ClassRegistry::init('User');
$q = "SELECT setval('users_id_seq',(SELECT MAX(id) from users))";
$this->User->query($q);
}
結果・・・だめ!postgresのシーケンスを進めても、cakephp側で持ってるidも進めないと結局1をinsertしようとする。これはこれで、対策ありそうやけど(setID()的な)見つけれず断念。
fixture時にsequenceリセットする
ググるとこんな記事を発見し、参考にしながら実践
AppCakeTest.phpをapp/Test/Fixture/ に作成
fields)
&& $this->fields['id']['type'] == 'integer'
&& $this->fields['id']['key'] == 'primary'
) {
$db->fetchAll("SELECT pg_catalog.setval(pg_get_serial_sequence('{$this->table}', 'id')"
. ", (SELECT MAX(id) FROM {$this->table}), true);", false);
}
return $result;
}
}
idというレコードがあって、かつ、interger でprimaryだったら、setvalで今の値を取ってくる、って処理かな
各FixtureをAppTextFixtureで継承
この部分を
class HogeFixture extends CakeTestFixture {
(以下略)
これに変更
App::uses("AppTestFixture","Test/Fixture");
class HogeFixture extends AppTestFixture {
(以下略)
これで、完了。で、結果は・・・OK!!!いけました。単体のテストでも、ALLな全テストでもこれで通りました。参考先の記事に本当助けられました。
学んだ事
試行錯誤して学んだ事
App:::usesってなんでも読み込めるんだなー
Fixtureって継承して拡張できるんだなー
mysqlだったらこの問題はおきない?(未検証)
初めてのcakephp + postgres なので、今後も何か起こるかも!?起きないかも!?