Come interrogare le query del database di test in base alla data corrente?

1

Esiste un modo fattibile per testare query SQL che contengono cose come CURRENT_DATE e NOW() in un test di unità?

Ad esempio:

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

E nel mio test di unità:

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);
    }
}

Ho taggato PHPUnit ma questa domanda dovrebbe essere abbastanza generica per qualsiasi tipo di test del database.

Per gli usi di derisione di cose come new \DateTime() (predefinito a "now"), posso cambiare la funzione per accettare un parametro. Suppongo di poter modificare tutte le query per prendere un parametro anziché NOW() o le funzioni dipendenti dal tempo, ma non sono sicuro che la modifica di tutte le query per questo scopo sia la giusta via da seguire, soprattutto per le cose che non lo sono ha senso passare in date / orari arbitrari. Inoltre, l'uso di parametri significherebbe che devo costruire i valori di quei parametri nell'applicazione e passarli dentro.

    
posta rink.attendant.6 15.03.2018 - 19:20
fonte

1 risposta

4

Rendi le funzioni che dovrebbero usare "data corrente" per ricevere un parametro e impostarlo sulla data corrente. Qualcosa come sotto:

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

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

Ora puoi passare una data fissa come "data corrente" nei test, o semplicemente non passare nulla ed eseguire altrimenti la data corrente effettiva.

Un altro approccio popolare è di non utilizzare now() tranne in un posto e utilizzare invece il tuo myNow() ovunque:

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;
}

A seconda della versione di myNow() disponibile nel contesto globale, hai tempo corrente o tempo costante prevedibile.

Al posto della funzione di livello più alto, puoi utilizzare un oggetto di configurazione condiviso / di livello superiore più specifico, che è anche uno schema popolare:

...execute($global_config->now());
    
risposta data 15.03.2018 - 20:37
fonte

Leggi altre domande sui tag