cakephp2.2 + PHPUnit + postgres でsequenceがうまく動かない場合の対処方法

2013/01/29

ちょっとハマったのでメモ

発端は

  • 一ファイル毎のテストは成功するのに、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 なので、今後も何か起こるかも!?起きないかも!?

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です