# Tester WP-Cron et le code dépendant de la date sans attendre des heures

> Déclencher un événement planifié à la demande, figer le temps courant et vérifier les échéances d'un plugin d'abonnements en quelques lignes de PHPUnit.

- Auteur : Clément Hadrot
- Publié le : 2020-05-07
- Mis à jour le : 2020-05-07
- Catégorie : Tests
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tests/tester-wp-cron-figer-le-temps-phpunit/

## L’essentiel

- Déclencher un hook cron sans attendre son échéance
- Figer le temps avec un filtre plutôt qu'un sleep
- Vérifier les événements planifiés en base de test

Problème classique sur un plugin d'abonnements : la logique de relance envoyait un e-mail « votre abonnement expire dans trois jours » via un événement planifié avec `wp_schedule_event()`. Impossible d'attendre trois jours à chaque exécution de la suite de tests pour vérifier que la relance partait bien au bon moment. La solution ne consiste pas à attendre, mais à déclencher l'événement directement et à contrôler ce que le code fait de la date courante.

WordPress ne propose pas d'API dédiée au figeage du temps comme Carbon le fait en PHP générique, mais deux leviers suffisent dans l'immense majorité des cas : appeler directement le hook cron plutôt que d'attendre son échéance, et filtrer la valeur retournée par les fonctions de date pour simuler « demain » ou « dans trois jours ».

## Déclencher un événement planifié directement

Un événement cron WordPress n'est jamais qu'une action déclenchée par un hook nommé. Plutôt que d'attendre que `wp_schedule_event()` arrive à échéance, il suffit d'appeler `do_action()` sur le même hook :

```
class Test_Relance_Abonnement extends WP_UnitTestCase {

    public function test_envoie_la_relance_a_trois_jours() {
        $post_id = self::factory()->post->create( array(
            'post_type' => 'abonnement',
            'meta_input' => array( '_date_expiration' => '2020-05-10' ),
        ) );

        // Déclenche directement le hook, sans attendre le cron réel.
        do_action( 'mon_plugin_verifier_expirations' );

        $envoyes = get_post_meta( $post_id, '_relance_envoyee', true );
        $this->assertEquals( '1', $envoyes );
    }
}
```

C'est un test d'intégration classique, mais il ne prouve qu'une chose : que le callback fonctionne à un instant T. Le vrai défi reste de simuler « le 7 mai » pour vérifier que la relance ne part ni trop tôt ni trop tard.

## Figer le temps avec un filtre plutôt qu'un sleep

WordPress construit ses dates autour de `current_time()` et de l'option `gmt_offset`, mais la brique la plus simple à filtrer reste souvent le point d'entrée que votre propre code utilise pour obtenir « maintenant ». La meilleure pratique consiste justement à faire transiter ce « maintenant » par un filtre dédié dans votre plugin, précisément pour le rendre testable :

```
// Dans le plugin :
function mon_plugin_maintenant() {
    return apply_filters( 'mon_plugin_maintenant', current_time( 'timestamp' ) );
}
```

> L'essentiel à retenir : Déclencher un hook cron sans attendre son échéance ; Figer le temps avec un filtre plutôt qu'un sleep ; Vérifier les événements planifiés en base de test

```
// Dans le test :
public function test_ne_relance_pas_trop_tot() {
    add_filter( 'mon_plugin_maintenant', function () {
        return strtotime( '2020-05-06' );
    } );

    do_action( 'mon_plugin_verifier_expirations' );

    remove_all_filters( 'mon_plugin_maintenant' );

    $this->assertEmpty( get_post_meta( $this->post_id, '_relance_envoyee', true ) );
}
```

Cette approche évite de dépendre d'une bibliothèque de mock du temps système : elle rend le code lui-même testable en explicitant sa dépendance à la date courante. C'est un principe qui dépasse largement WP-Cron.

## Vérifier qu'un événement est bien planifié

Au-delà du déclenchement, il est utile de vérifier que l'événement a été correctement enregistré dans la table des options (`cron`), sans quoi il ne se déclenchera jamais en production :

- `wp_next_scheduled( 'mon_plugin_verifier_expirations' )` doit retourner un timestamp, pas `false`
- `wp_get_scheduled_event( 'mon_plugin_verifier_expirations' )` permet d'inspecter la récurrence choisie (`daily`, `hourly`, etc.)
- Un test d'activation du plugin doit vérifier que `wp_schedule_event()` a bien été appelé une seule fois, pour éviter les doublons d'événements à chaque réactivation

## Piège fréquent : oublier de nettoyer entre les tests

La table cron est stockée dans les options, donc `WP_UnitTestCase` la réinitialise normalement entre chaque test grâce à la transaction annulée en fin de test. Le vrai piège survient quand le code planifie l'événement dans un hook `init` global : sans `wp_clear_scheduled_hook()` explicite en fin de test, des événements peuvent s'accumuler d'un test à l'autre et fausser des assertions sur le nombre d'événements planifiés.

## Notre verdict

Tester du code dépendant du temps sous WordPress ne demande ni bibliothèque tierce ni sleep artificiel : déclencher directement le hook cron et faire transiter le « maintenant » par un filtre couvre l'essentiel des scénarios. La conception même du système cron — pourquoi WP-Cron se déclenche sur le trafic plutôt qu'en tâche système — reste un autre sujet, à part.
