web-dev-qa-db-ja.com

現在の日付に基づいてデータベースクエリを単体テストするにはどうすればよいですか?

単体テストで_CURRENT_DATE_やNOW()などを含むSQLクエリをテストする実行可能な方法はありますか?

例えば:

_public function deletePastEntries(): bool {
    $sql = "DELETE FROM queue WHERE period_end < NOW();";
    return $this->db->query($sql)->execute();
}
_

そして私の単体テストでは:

_class QueueTest extends AbstractDatabaseTestCase
{
    // ...<snip>...boilerplate stuff

    public function testDeletePastEntries(): void {
        $this->object->deletePastEntries();

        $actualTable = $this->getConnection()->createQueryTable('queue', 'SELECT id, user, period_start, period_end FROM queue ORDER BY id;');

        $expectedTable = $this->createArrayDataSet([
            'queue' => [/* stuff that should remain */]
        ])->getTable('queue');

        static::assertTablesEqual($expectedTable, $actualTable);
    }
}
_

PHPUnitにタグを付けましたが、この質問はあらゆる種類のデータベーステストに十分に一般的である必要があります。

new \DateTime()(デフォルトは「now」)などのモックの使用法については、パラメーターを受け入れるように関数を変更できます。すべてのクエリを変更してNOW()または時間依存の関数の代わりにパラメータを取ることができると思いますが、この目的のためにすべてのクエリを変更することが今後の正しい方法であるかどうかはわかりません任意の日付/時刻で渡すのが意味をなさないもの。さらに、パラメーターを使用するということは、アプリケーションでそれらのパラメーターの値を作成し、それらを渡す必要があることを意味します。

1

「現在の日付」を使用する必要のある関数がそのパラメーターを受け取り、デフォルトで現在の日付に設定するようにします。以下のようなもの:

_public function deletePastEntries($now = null)
{
    if (! $now) {
        $now = time();
    }

    $sql = "DELETE FROM queue WHERE period_end < ?;";
    return $this->db->query($sql)->execute($now);
}
_

これで、テストで「現在の日付」として固定の日付を渡すか、何も渡さずに実際の現在の日付で実行することができます。

別の一般的なアプローチは、1つの場所を除いてnow()を使用せず、代わりに独自のmyNow()をどこでも使用することです。

_public function deletePastEntries()
{
    $sql = "DELETE FROM queue WHERE period_end < ?;";
    return $this->db->query($sql)->execute(myNow());
}

// Elsewhere:
public function myNow() {
  return now();
}

// For tests, redefine it:
public function myNow() {
  return WELL_KNOWN_MOMENT_CONST;
}
_

グローバルコンテキストで使用できるmyNow()のバージョンに応じて、現在の時刻または予測可能な一定の時刻のいずれかがあります。

最上位の関数の代わりに、より具体的な共有/最上位の構成オブジェクトを使用できます。これも一般的なパターンです。

_...execute($global_config->now());
_
4
9000