ちょっとハマったのでメモ
発端は
- 一ファイル毎のテストは成功するのに、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 なので、今後も何か起こるかも!?起きないかも!?