Question

Je travaille actuellement avec PHPUnit pour essayer de développer des tests parallèlement à ce que j'écris. Cependant, je travaille actuellement sur l'écriture de Session Manager et j'ai des problèmes pour le faire ...

Le constructeur de la classe de traitement de session est

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

Cependant, comme PHPUnit envoie du texte avant de commencer les tests, tout test effectué sur cet objet renvoie un test ayant échoué, comme HTTP " en-têtes " HTTP ont été envoyés ...

Était-ce utile?

La solution

Eh bien, votre gestionnaire de session est fondamentalement cassé 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 généralisée de l'état global ( echo , en-tête , exit , session_start etc. etc.).

La meilleure chose à faire est d’isoler les effets secondaires d’un composant, qui peuvent être permutés au moment de l’exécution. De cette façon, vos tests peuvent utiliser des objets fictifs, alors que le code en direct utilise des adaptateurs qui ont des effets secondaires réels. Vous constaterez que cela ne fonctionne pas bien avec les singletons, que je suppose que vous utilisez. Vous devrez donc utiliser un autre mécanisme pour obtenir les objets partagés distribués dans votre code. Vous pouvez commencer avec un registre statique, mais il existe des solutions encore meilleures si cela ne vous dérange pas d'apprendre un peu.

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

Autres conseils

Créez un fichier d'amorçage pour phpunit, qui appelle:

session_start();

Puis lancez phpunit comme ceci:

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

Le fichier d'amorçage est appelé avant tout le reste, donc l'en-tête n'a pas été envoyé et tout devrait bien fonctionner.

phpUnit imprime la sortie au fur et à mesure que les tests sont exécutés, ce qui entraîne le retour de true à headers_sent (), même lors de votre premier test.

Pour résoudre ce problème pour toute une suite de tests, vous devez simplement utiliser ob_start () dans votre script d'installation.

Par exemple, disons que vous avez un fichier nommé AllTests.php qui est la première chose chargée par phpUnit. Ce script pourrait ressembler à ceci:

<?php

ob_start();

require_once 'YourFramework/AllTests.php';

class AllTests {
    public static function suite() {
        $suite = new PHPUnit_Framework_TestSuite('YourFramework');
        $suite->addTest(YourFramework_AllTests::suite());
        return $suite;
    }
}

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

phpunit --stderr /path/to/your/test

J'espère que ça aide quelqu'un!

Je pense que le " right " La solution consiste à créer une classe très simple (si simple qu'il n'a pas besoin d'être testée), qui encapsule les fonctions liées à la session de PHP, et l'utiliser au lieu d'appeler session_start () , etc. directement .

Dans l'objet test mock pass au lieu d'une véritable classe de session avec état et non testable.

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

Je me demande pourquoi personne n'a répertorié l'option XDebug:

/**
 * @runInSeparateProcess
 * @requires extension xdebug
 */
public function testGivenHeaderIsIncludedIntoResponse()
{
    $customHeaderName = 'foo';
    $customHeaderValue = 'bar';

    // Here execute the code which is supposed to set headers
    // ...

    $expectedHeader = $customHeaderName . ': ' . $customHeaderValue;
    $headers = xdebug_get_headers();

    $this->assertContains($expectedHeader, $headers);
}

Ne pouvez-vous pas utiliser la mise en mémoire tampon de sortie avant de commencer le test? Si vous mettez en mémoire tampon tout ce qui est produit en sortie, vous ne devriez pas rencontrer de problèmes pour définir des en-têtes, car aucun résultat n’aurait été envoyé au client à ce moment-là.

Même si OB est utilisé quelque part dans vos classes, il est empilable et OB ne devrait pas affecter ce qui se passe à l'intérieur.

Autant que je sache, Zend Framework utilise la même mise en mémoire tampon de sortie pour leurs tests de paquet Zend_Session. Vous pouvez consulter leurs scénarios de test pour vous aider à démarrer.

La création du fichier d’amorçage, soulignait 4 posts, semble être la solution la plus propre.

Souvent, avec PHP, nous devons maintenir et essayer d’ajouter une sorte de discipline d’ingénierie aux projets existants qui sont extrêmement mal organisés. Nous n'avons ni le temps (ni l'autorité) d'abandonner tout le tas d'ordures et de recommencer, de sorte que la première réponse de troelskn n'est pas toujours possible comme moyen d'aller de l'avant. (Si nous pouvions revenir à la conception initiale, nous pourrions abandonner PHP et utiliser quelque chose de plus moderne, tel que ruby ??ou python, plutôt que de contribuer à perpétuer ce COBOL du monde du développement Web.)

Si vous essayez d'écrire des tests unitaires pour les modules qui utilisent session_start ou setcookie au travers de ceux-ci, le fait de démarrer la session dans un fichier boostrap vous évitera ces problèmes.

Alors que j'effectue un peu mon bootstrap (oui, je sais que la plupart d'entre vous ne le font pas), je rencontre le même problème (les deux en-tête () et session_start ()). La solution que j'ai trouvée est assez simple: définissez une constante dans votre amorçage unittest et vérifiez-la simplement avant d'envoyer l'en-tête ou de démarrer la session:

// phpunit_bootstrap.php
define('UNITTEST_RUNNING', true);

// bootstrap.php (application bootstrap)
defined('UNITTEST_RUNNING') || define('UNITTEST_RUNNING', false);
.....
if(UNITTEST_RUNNING===false){
    session_start();
}

Je conviens que ce n’est pas parfait, mais je néglige une application existante, la réécriture de grandes pièces n’est pas souhaitée. J'utilise également la même logique pour tester des méthodes privées à l'aide des méthodes magiques __call () et __set ().

public function __set($name, $value){
    if(UNITTEST_RUNNING===true){
       $name='_' . $name;
       $this->$name=$value;
    }
    throw new Exception('__set() can only be used when unittesting!');
 }

Il semble que vous deviez injecter la session pour pouvoir tester votre code. La meilleure option que j'ai utilisée est Aura.Auth pour le processus d'authentification et l'utilisation de NullSession et NullSegment pour les tests.

Test d'aura avec sessions nulles

Le cadre Aura est magnifiquement écrit et vous pouvez utiliser Aura.Auth seul, sans aucune autre dépendance du cadre Aura.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top