76 votes

Puis-je "simuler" le temps dans PHPUnit ?

... sans savoir si "se moquer" est le bon mot.

Quoi qu'il en soit, j'ai une base de code héritée pour laquelle j'essaie d'écrire des tests qui sont basés sur le temps. J'essaie de ne pas être trop vague, le code est lié à l'examen de l'historique d'un élément et à la détermination du fait que cet élément a maintenant basé un seuil de temps.

À un moment donné, je dois également tester l'ajout de quelque chose à cet historique et vérifier que le seuil est maintenant modifié (et, évidemment, correct).

Le problème que je rencontre est qu'une partie du code que je teste utilise des appels à time() et j'ai donc beaucoup de mal à savoir exactement quel devrait être le seuil de temps, étant donné que je ne sais pas exactement quand la fonction time() va être appelée.

Ma question est donc la suivante : existe-t-il un moyen pour moi d'outrepasser l'appel time(), ou d'une manière ou d'une autre de simuler l'heure, de sorte que mes tests fonctionnent dans un "temps connu" ?

Ou dois-je simplement accepter le fait que je vais devoir faire quelque chose dans le code que je teste, pour me permettre de le forcer à utiliser une heure particulière si nécessaire ?

D'une manière ou d'une autre, existe-t-il des "pratiques communes" pour développer des fonctionnalités sensibles au facteur temps qui soient faciles à tester ?

Modifier : Une partie de mon problème, aussi, est le fait que le moment où les choses se sont produites dans l'histoire affecte le seuil. Voici un exemple d'une partie de mon problème...

Imaginez que vous avez une banane et que vous essayez de savoir quand elle doit être mangée. Disons qu'elle expire dans les 3 jours, à moins qu'elle n'ait été pulvérisée avec un produit chimique, auquel cas nous ajoutons 4 jours à la date d'expiration, à partir du moment où le spray a été appliqué . Ensuite, nous pouvons ajouter 3 mois supplémentaires en le congelant, mais s'il a été congelé, nous n'avons qu'un jour pour l'utiliser après sa décongélation.

Toutes ces règles sont dictées par des chronologies historiques. Je conviens que je pourrais utiliser la suggestion de Dominik de tester en quelques secondes, mais qu'en est-il de mes données historiques ? Devrais-je simplement les "créer" à la volée ?

Comme vous pouvez le constater ou non, j'essaie toujours d'appréhender ce concept de "test" ;)

1 votes

Pour PHP7, vous pouvez utiliser github.com/runkit7/Timecop-PHP qui est basé sur runkit7

64voto

fab Points 9058

J'ai récemment trouvé une autre solution qui est idéale si vous utilisez les espaces de noms de PHP 5.3. Vous pouvez implémenter une nouvelle fonction time() dans votre espace de noms actuel et créer une ressource partagée où vous définissez la valeur de retour dans vos tests. Ainsi, tout appel non qualifié à time() utilisera votre nouvelle fonction.

Pour une lecture plus approfondie, je l'ai décrite en détail dans mon livre intitulé blog

1 votes

J'aime vraiment cette idée Fabian. Un avantage supplémentaire est que cela oblige mes collègues de travail à passer à la version 5.3 ;)

0 votes

C'est extrêmement utile, merci d'avoir partagé cette technique avec nous Fabian - très apprécié !

0 votes

Travail de génie ici. Les espaces de noms pour les fonctions rendent utile la substitution de fonctions intégrées pour les tests.

3voto

Dominik Points 931

Personnellement, je continue à utiliser time() dans les fonctions/méthodes testées. Dans votre code de test, assurez-vous de ne pas tester l'égalité avec time(), mais simplement une différence de temps inférieure à 1 ou 2 (selon le temps d'exécution de la fonction).

0 votes

Pour l'instant, il semble que c'est la voie que je vais devoir suivre. J'ai également ajouté un "exemple" à mon problème, au cas où cela serait utile. Merci.

0 votes

J'ai vu votre exemple. Encore une fois, personnellement, pour ce genre de tests, j'utilise la méthode de configuration de phpunit pour préparer les données historiques "correctes" (par exemple dans la base de données).

2 votes

Cela rendra vos tests très fragiles. Ils peuvent échouer apparemment sans raison, dès que le processus testé subit un retard (pour quelque raison que ce soit).

2voto

Vlad Balmos Points 2552

Vous pouvez remplacer la fonction time() de php en utilisant l'extension runkit. Assurez-vous de définir runkit.internal_overide sur On.

2voto

jhvaras Points 391

Utilisation de l'extension [runkit][1] :

define('MOCK_DATE', '2014-01-08');
define('MOCK_TIME', '17:30:00');
define('MOCK_DATETIME', MOCK_DATE.' '.MOCK_TIME);

private function mockDate()
{
    runkit_function_rename('date', 'date_real');
    runkit_function_add('date','$format="Y-m-d H:i:s", $timestamp=NULL', '$ts = $timestamp ? $timestamp : strtotime(MOCK_DATETIME); return date_real($format, $ts);');
}

private function unmockDate()
{
    runkit_function_remove('date');
    runkit_function_rename('date_real', 'date');
}

Vous pouvez même tester le simulateur comme ceci :

public function testMockDate()
{
    $this->mockDate();
    $this->assertEquals(MOCK_DATE, date('Y-m-d'));
    $this->assertEquals(MOCK_TIME, date('H:i:s'));
    $this->assertEquals(MOCK_DATETIME, date());
    $this->unmockDate();
}

1voto

Milan Babuškov Points 20423

La solution la plus simple serait de remplacer la fonction PHP time() par votre propre version. Cependant, vous ne pouvez pas remplacer facilement les fonctions PHP intégrées ( voir ici ).

Sinon, le seul moyen est d'abstraire l'appel time() vers une classe/fonction propre qui renverrait le temps dont vous avez besoin pour les tests.

Vous pouvez également exécuter le système de test (système d'exploitation) dans une machine virtuelle et modifier l'heure de tout l'ordinateur virtuel.

0 votes

Merci Milan : si je suis d'accord pour dire que l'exécution dans une VM serait une option pour " forcer " le temps, je pense que je devrais toujours tenir compte des " variables d'exécution " et donc faire ce que Dominik a suggéré. Mais l'idée est intéressante, merci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X