47 votes

Test unitaire avec des éléments qui doivent envoyer des en-têtes

Je travaille actuellement avec PHPUnit pour essayer de développer des tests en même temps que ce que j'écris, cependant, je travaille actuellement sur l'écriture du gestionnaire de session, et j'ai des problèmes à le faire...

Le constructeur de la classe de gestion de session est

private function __construct()
{
    if (!headers_sent())
    {
        session_start();
        self::$session_id = session_id();
    }
}

Cependant, puisque PHPUnit envoie du texte avant de commencer les tests, tout test sur cet objet renvoie un test échoué, car les "Headers" HTTP ont déjà été envoyés...

0 votes

Je viens de rencontrer le même problème, mais aucune des réponses ci-dessous ne fonctionne. Qu'as-tu finalement fait? S'il te plaît envoie-moi un email.

38voto

troelskn Points 51966

Eh bien, votre gestionnaire de session est essentiellement défectueux par conception. Pour pouvoir tester quelque chose, il doit être possible de l'isoler des effets secondaires. Malheureusement, PHP est conçu de telle manière qu'il encourage l'utilisation libérale de l'état global (echo, header, exit, session_start etc. etc.).

La meilleure chose que vous puissiez faire est d'isoler les effets secondaires dans un composant qui peut être remplacé à l'exécution. De cette façon, vos tests peuvent utiliser des objets simulés, tandis que le code en direct utilise des adaptateurs ayant de réels effets secondaires. Vous verrez que cela ne fonctionne pas bien avec les singletons, que je présume vous utilisez. Vous devrez donc utiliser un autre mécanisme pour distribuer les objets partagés à votre code. Vous pouvez commencer par un registre statique, mais il existe même de meilleures solutions si vous n'êtes pas contre un peu d'apprentissage.

Si vous ne pouvez pas le faire, vous avez toujours la possibilité d'écrire des tests d'intégration. Par exemple, utilisez l'équivalent de PHPUnit de WebTestCase.

0 votes

Merci, j'avais besoin de cette approche dure! Mon design est sans aucun doute meilleur maintenant que j'ai isolé les effets environnementaux du reste de ma classe.

1 votes

La documentation pour WebTestCase est maintenant disponible sur simpletest.org/en/web_tester_documentation.html.

20voto

Dominik Points 111

Créez un fichier bootstrap pour phpunit, qui appelle :

session_start();

Ensuite, lancez phpunit comme ceci :

phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/

Le fichier bootstrap est appelé avant tout le reste, donc l'en-tête n'a pas été envoyé et tout devrait fonctionner correctement.

1 votes

Si vous utilisez un fichier de configuration XML, vous pouvez configurer le bootstrap en ajoutant un attribut bootstrap à votre élément racine comme ceci : ...

19voto

Michael Points 159

PhpUnit imprime la sortie pendant l'exécution des tests, ce qui fait que headers_sent() retourne true même dans votre premier test.

Pour surmonter ce problème pour l'ensemble d'une suite de tests, il vous suffit d'utiliser ob_start() dans votre script de configuration.

Par exemple, supposons que vous ayez un fichier nommé AllTests.php qui est le premier élément chargé par phpUnit. Ce script pourrait ressembler à ce qui suit:

addTest(YourFramework_AllTests::suite());
        return $suite;
    }
}

2 votes

Meilleure solution pour tester le code PHP de boîte noire que vous ne pouvez pas modifier.

0 votes

Notez que PHP 3.6 le fait maintenant automatiquement.

2 votes

Cette solution a fonctionné pour moi :D PHPUnit 3.6 est censé implémenter cela mais n'a pas fonctionné correctement pour moi. J'ai ajouté ob_start(); dans mon fichier bootstrap.php et tous mes tests avec les cookies et les en-têtes ont recommencé à fonctionner correctement.

13voto

J'ai rencontré le même problème et je l'ai résolu en appelant phpunit avec le drapeau --stderr comme ceci :

phpunit --stderr /chemin/vers/votre/test

J'espère que cela aidera quelqu'un !

0 votes

Après une heure ou deux de recherche, c'était la solution la plus simple et la meilleure pour me permettre de tester du code qui avait setcookie() enfoui à l'intérieur. L'utilisation de @runInSeparateProcess entraîne d'autres problèmes, tandis que ceci est parfait (pour l'instant).

5voto

porneL Points 42805

Je pense que la solution "correcte" est de créer une classe très simple (tellement simple qu'elle n'a pas besoin d'être testée) qui est un wrapper pour les fonctions liées à la session de PHP, et l'utiliser au lieu d'appeler session_start(), etc. directement.

Lors du test, passez un objet mock à la place d'une vraie classe de session étatique et non testable.

private function __construct(SessionWrapper $wrapper)
{
   if (!$wrapper->headers_sent())
   {
      $wrapper->session_start();
      $this->session_id = $wrapper->session_id();
   }
}

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