LaravelでPDOのモックテスト2
【Laravel】DBエラー時のシナリオのテスト - 勉強日記の続き
モチベーション
- DBエラー時の雨の日シナリオのテストをしたい
PDOモック作成・セット側
<?php ... trait MockPdo { /** * コネクション名を指定し、当該コネクションのPDOをモックする * @param string $connectionName コネクション名 * @return MockInterface|PDO PDOのProxy Partial Mock */ protected function mockPdo(string $connectionName = null): MockInterface { $connection = DB::connection($connectionName); $pdo = $connection->getPdo(); /** * Proxy Partial Mock * @var MockInterface $pdoMock */ $pdoMock = Mockery::mock($pdo)->makePartial(); // ---------------------------------------- // Connection@setPdo($pdo)の中で // $this->transactions = 0; // されるとテストケース終了時に制御が返ってこなくなる // $this->transactionsの値を退避し、リフレクションで再セットする // ---------------------------------------- $reflectionClass = new ReflectionClass(get_class($connection)); $transactionsAccessor = $reflectionClass->getProperty('transactions'); $transactionsAccessor->setAccessible(true); $transactions = $transactionsAccessor->getValue($connection); $connection->setPdo($pdoMock); $transactionsAccessor->setValue($connection, $transactions); return $pdoMock; } }
- DSNの設定等が面倒なので、
Connection@getPdo()
でPDOを引っこ抜いてMockeryでProxy Partial Mockを作る - Proxy Partial Mockを
Connection@setPdo($pdo)
に再セットする際、Connection@transactions
メンバ変数が0になってしまう Connection@transactions
は入れ子のトランザクションの深さを管理している- RefreshDatabaseトレイト使用時、トランザクションの中で
Connection@transactions
メンバ変数が0になってしまうと、テストケースのtearDown()
で制御が帰ってこなくなることがある- 詳細条件不明…
- なので、
Connection@setPdo($pdo)
呼び出し前のConnection@transactions
の値を退避しておき、再セットする - 当該メンバはprotectedなのでリフレクションを使用
PDOモックのexpectation記述側
<?php ... /** * @test */ public function articles_id_DBエラー時boo() { // ---------------------------------------- // 1. setup // クエリ発行時にPDOExceptionが発生するようにPDOをモック // ---------------------------------------- $pdoMock = $this->mockPdo(); $pdoMock->shouldReceive('prepare') ->with(Mockery::on(function (string $stmt): bool { return preg_match('/select.*articles/', $stmt) === 1; })) ->andThrow(new \PDOException); // ---------------------------------------- // 2. action and assertion // ---------------------------------------- $this->get('/articles/999')->assertSeeText('boo!'); }
- expectation
- articlesテーブルにselectで問い合わせたときPDOExceptionを送出する
- それ以外は元気に動く
- preg_match程度なら
Mockery::pattern($regex)
が使える