ちょっとハマったのでメモ
発端は
- 一ファイル毎のテストは成功するのに、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/ に作成
<?php
class AppTestFixture extends CakeTestFixture
{
function insert(&$db)
{
$result = parent::insert($db);
if (array_key_exists('id', $this->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 なので、今後も何か起こるかも!?起きないかも!?