Testen Sie PHP-Header mit PHPUnit


97

Ich versuche, mit PHPunit eine Klasse zu testen, die einige benutzerdefinierte Header ausgibt.

Das Problem ist, dass auf meinem Computer Folgendes:

<?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);
    }
}

oder sogar das:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

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

        ob_clean();
    }
}

Geben Sie diesen Fehler zurück:

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.

Dies sieht so aus, als würde vor dem Ausführen des Tests etwas anderes auf dem Terminal ausgegeben, obwohl keine andere Datei enthalten ist und vor dem Beginn des PHP-Tags kein anderes Zeichen steht. Könnte es etwas in PHPunit sein, das dies verursacht?

Was könnte das Problem sein?


14
Ich wollte dies nur behandeln, wenn es auch andere Leute gibt, die daran interessiert sind. headers_list () funktioniert nicht, während PHPunit (das PHP CLI verwendet) ausgeführt wird, aber xdebug_get_headers () funktioniert stattdessen.
Titel

Antworten:


123

Das Problem ist, dass PHPUnit einen Header auf dem Bildschirm druckt und Sie zu diesem Zeitpunkt keine weiteren Header hinzufügen können.

Die Problemumgehung besteht darin, den Test in einem isolierten Prozess auszuführen. Hier ist ein Beispiel

<?php

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

Dies führt zu:

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

Der Schlüssel ist die Annotation @runInSeparateProcess.

Wenn Sie PHPUnit ~ 4.1 oder etwas verwenden und den Fehler erhalten:

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

Versuchen Sie, dies Ihrer Bootstrap-Datei hinzuzufügen, um das Problem zu beheben:

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

7
Dies führt zu Fehlern, da einige define () -Anweisungen vorhanden sind. PHPUnit_Framework_Exception: Hinweis: Konstante xyz bereits definiert
Minhaz

1
@mebjas Das klingt nicht verwandt.
SamHennessy

4
Ich habe dies bei der Bewerbung erhalten:PHP Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'SplFileInfo' is not allowed' in phar:///usr/local/bin/phpunit/phpunit/Util/GlobalState.php:211
t1gor

2
Dies ist definitiv die beste Option, um das Problem zu lösen. Es hat wie ein Zauber funktioniert!
Xarlymg89

2
Ich musste xdebug_get_headers () verwenden, um das Array der gesetzten Header zu erhalten. Die globale Funktion headers_list () hat in meinem Fall nicht funktioniert.
Shalom Sam

107

Obwohl das Ausführen des Tests in einem separaten Prozess das Problem behebt, entsteht beim Ausführen einer großen Testsuite ein spürbarer Overhead.

Mein Fix war, die Ausgabe von phpunit wie folgt an stderr zu leiten:

phpunit --stderr <options>

Dies sollte das Problem beheben und bedeutet auch, dass Sie keine Wrapper-Funktion erstellen und alle Vorkommen in Ihrem Code ersetzen müssen.


3
Brillant! Keine Änderungen an meinem Code, keine separaten Prozesse und es funktioniert.
Alexfernandez

Aber sehe ich immer noch Fehler, die ich gemacht habe, und erhalte die Ausgabe von error_reporting (E_ALL)?
Spankmaster79

1
@ spankmaster79 Ja, es wird nur der Standardfehler Ihres Terminals angezeigt. Standardmäßig drucken die meisten Terminals Standardausdruck und Standardfehler zusammen aus, es handelt sich jedoch tatsächlich um separate Streams.
Jon Cairns

Ich wurde gemacht !!! tnx für diesen trick ist es sinnvoll, fehler sollten dem stderror gemeldet werden !!! Tnx
th3n3rd

42
Sie können stderr="true"Ihre phpunit.xml hinzufügen , um einige Tastenanschläge zu speichern.
Tszming

9

Nebenbei: Für mich wurden headers_list()immer wieder 0 Elemente zurückgegeben. Ich habe den Kommentar von @titel zu dieser Frage bemerkt und festgestellt , dass er hier besondere Erwähnung verdient:

Ich wollte dies nur behandeln, wenn es auch andere Leute gibt, die daran interessiert sind. headers_list()funktioniert nicht während der Ausführung von PHPunit (das PHP CLI verwendet), sondern xdebug_get_headers()funktioniert stattdessen.

HTH


4

Wie bereits in einem Kommentar erwähnt, ist es meiner Meinung nach eine bessere Lösung, processIsolation in der XML-Konfigurationsdatei wie zu definieren

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

Auf diese Weise müssen Sie die Option --stderr nicht übergeben, was Ihre Mitarbeiter irritieren könnte.


4
Es ist wahrscheinlich besser, es nur für den Test zu definieren, der es erfordert. Wenn Sie es für alle Tests festlegen, werden die Tests nur langsam ausgeführt.
Shi

3

Ich hatte eine radikalere Lösung, um sie $_SESSIONin meinen getesteten / enthaltenen Dateien zu verwenden. Ich habe eine der PHPUnit- Dateien unter ../PHPUnit/Utils/Printer.php bearbeitet , um "session_start();"vor dem Befehl "print $ buffer" eine zu haben .

Es hat bei mir wie ein Zauber funktioniert. Aber ich denke, die "Joonty" -Nutzerlösung ist die bisher beste von allen.


Haben Sie die Pull-Anfrage im Repository eingereicht? Ich denke, dies könnte ein häufiges Problem sein.
t1gor

Vielen Dank für den Hinweis, ich habe session_start () in meiner phpunit bootstrap.php aufgerufen und es funktioniert für mich
bumperbox

0

Eine alternative Lösung zu @runInSeparateProcess besteht darin, die Option --process -isolation anzugeben, wenn PHPUnit ausgeführt wird:

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

Dies ist analog zum Setzen der Option processIsolation = "true" in phpunit.xml.

Diese Lösung hat ähnliche Vor- und Nachteile wie die Angabe der Option --stderr, die in meinem Fall jedoch nicht funktioniert hat. Grundsätzlich sind keine Codeänderungen erforderlich, auch wenn die Ausführung jedes Tests in einem separaten PHP-Prozess zu Leistungseinbußen führen kann.


Dies ist möglicherweise ein erster Schritt, um die Ursache zu ermitteln und einige Tests durchzuführen. Um jedoch wartbare Tests zu erstellen, binden Sie die Anmerkung einfach direkt in die Testdatei ein. Je weniger spezielle Optionen über die Befehlszeile erforderlich sind, desto einfacher ist die Wartung einer CI-Systemkonfiguration.
Shi

wer hat jemals #fail herabgestuft. Diese Antwort ist als weitere Option korrekt.
fb

0

Verwenden Sie den Parameter --stderr, um nach Ihren Tests Header von PHPUnit abzurufen.

phpunit --stderr
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.