[https://wand-ta.hatenablog.com/entry/2019/11/01/212453:title]の続き
[https://github.com/wand2016/pdo_mock/blob/5.5/tests/Concerns/MockPdo.php:embed:cite]
モチベーション
- 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)
が使える