web-dev-qa-db-fra.com

Tester PHP en-têtes avec PHPUnit

J'essaie d'utiliser PHPunit pour tester une classe qui génère des en-têtes personnalisés.

Le problème est que sur ma machine ceci:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

        header('Location: foo');
        $headers_list = headers_list();
        header_remove();

        ob_clean();

        $this->assertContains('Location: foo', $headers_list);
    }
}

ou même ceci:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

        header('Location: foo');
        header_remove();

        ob_clean();
    }
}

renvoyer cette erreur:

name@Host [~/test]# phpunit --verbose HeadersTest.php 
PHPUnit 3.6.10 by Sebastian Bergmann.

E

Time: 0 seconds, Memory: 2.25Mb

There was 1 error:

1) HeadersTest::testHeaders
Cannot modify header information - headers already sent by (output started at /usr/local/lib/php/PHPUnit/Util/Printer.php:173)

/test/HeadersTest.php:9

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.

Cela ressemble à une autre sortie du terminal avant l'exécution du test, même si aucun autre fichier n'est inclus et qu'il n'y a pas d'autre caractère avant le début de la balise PHP. Cela pourrait-il être causé par quelque chose à l'intérieur de PHPunit?

Quel pourrait être le problème?

78
titel

Le problème est que PHPUnit imprimera un en-tête à l’écran et qu’à ce stade, vous ne pourrez plus en ajouter.

La solution consiste à exécuter le test dans un processus isolé. Voici un exemple

<?php

class FooTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     */
    public function testBar()
    {
        header('Location : http://foo.com');
    }
}

Cela se traduira par:

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

La clé est l'annotation @runInSeparateProcess. 

Si vous utilisez PHPUnit ~ 4.1 ou quelque chose et obtenez l'erreur:

PHP Fatal error:  Uncaught Error: Class 'PHPUnit_Util_Configuration' not found in -:378
Stack trace:
#0 {main}
  thrown in - on line 378

Fatal error: Uncaught Error: Class 'PHPUnit_Util_Configuration' not found in - on line 378

Error: Class 'PHPUnit_Util_Configuration' not found in - on line 378

Call Stack:
    0.0013     582512   1. {main}() -:0

Essayez d'ajouter ceci à votre fichier d'amorçage pour résoudre le problème:

<?php
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
    define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/path/to/composer/vendors/dir/autoload.php');
}
106
SamHennessy

Bien que l'exécution du test dans un processus séparé résolve le problème, il existe un temps système considérable lors de l'exécution d'une grande suite de tests.

Ma solution était de diriger la sortie de phpunit vers stderr, comme ceci:

phpunit --stderr <options>

Cela devrait résoudre le problème. Cela signifie également que vous n'avez pas à créer de fonction wrapper et à remplacer toutes les occurrences de votre code.

96
Jon Cairns

En passant: Pour moi, headers_list() renvoyait 0 élément. J'ai remarqué le commentaire de @titel sur la question et pensé qu'il méritait une mention spéciale ici:

Je voulais juste couvrir cela s'il y a d'autres personnes intéressées dans cela aussi. headers_list() ne fonctionne pas avec PHPunit (qui utilise PHP CLI) mais xdebug_get_headers() fonctionne à la place.

HTH

8
Melle

J'avais une solution plus radicale, afin d'utiliser $_SESSION dans mes fichiers testés/inclus . J'ai édité l'un des fichiers PHPUnit dans .. /PHPUnit/Utils/Printer.php pour avoir un "session_start();" avant la commande "print $ buffer"

Cela a fonctionné pour moi comme un charme. Mais je pense que la solution utilisateur "joonty" est la meilleure de toutes à ce jour.

3
Sergio Abreu

Comme déjà mentionné dans un commentaire, je pense que la meilleure solution serait de définir processIsolation dans le fichier de configuration XML comme 

     <?xml version="1.0" encoding="UTF-8"?>
     <phpunit
        processIsolation            = "true"
        // ... 
     >
     </phpunit>

De cette manière, vous n'avez pas à passer l'option --stderr, ce qui pourrait irriter vos collègues. 

0
PepeNietnagel

Une solution alternative à @runInSeparateProcess consiste à spécifier l'option --process-isolation lors de l'exécution de PHPUnit:

name@Host [~/test]# phpunit --process-isolation HeadersTest.php

Cela revient à définir l’option processIsolation = "true" dans phpunit.xml.

Cette solution présente des avantages/inconvénients similaires à la spécification de l'option --stderr, qui n'a toutefois pas fonctionné dans mon cas. En principe, aucune modification de code n'est nécessaire, même s'il peut y avoir un impact négatif sur les performances en raison de l'exécution de chaque test dans un processus PHP distinct.

0
AlexB