Prova le intestazioni PHP con PHPUnit


97

Sto cercando di utilizzare PHPunit per testare una classe che genera alcune intestazioni personalizzate.

Il problema è che sulla mia macchina questo:

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

o anche questo:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

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

        ob_clean();
    }
}

restituisci questo errore:

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.

Sembra che ci sia qualcos'altro che viene inviato al terminale prima che il test venga eseguito anche se non ci sono altri file inclusi e non ci sono altri caratteri prima dell'inizio del tag PHP. Potrebbe essere qualcosa all'interno di PHPunit che sta causando questo?

Quale potrebbe essere il problema?


14
Volevo solo coprire questo se ci sono altre persone interessate anche a questo. headers_list () non funziona durante l'esecuzione di PHPunit (che utilizza PHP CLI) ma invece xdebug_get_headers () funziona.
titolo

Risposte:


123

Il problema è che PHPUnit stamperà un'intestazione sullo schermo ea quel punto non puoi aggiungere altre intestazioni.

La soluzione consiste nell'eseguire il test in un processo isolato. Ecco un esempio

<?php

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

Ciò si tradurrà in:

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

La chiave è l'annotazione @runInSeparateProcess.

Se stai usando PHPUnit ~ 4.1 o qualcosa del genere e ottieni l'errore:

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

Prova ad aggiungerlo al tuo file bootstrap per risolverlo:

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

7
questo causa errori in alcune istruzioni define () PHPUnit_Framework_Exception: Avviso: xyz costante già definito
Minhaz

1
@mebjas Sembra non correlato.
SamHennessy

4
Ho ricevuto questo quando applicato: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
Questa è sicuramente l'opzione migliore per risolvere il problema. Ha funzionato come un fascino!
xarlymg89

2
Ho dovuto usare xdebug_get_headers (), per ottenere l'array di intestazioni impostate. La funzione globale headers_list () non ha funzionato nel mio caso.
Shalom Sam

107

Sebbene l'esecuzione del test in un processo separato risolva il problema, c'è un notevole sovraccarico quando si esegue una vasta suite di test.

La mia soluzione era dirigere l'output di phpunit su stderr, in questo modo:

phpunit --stderr <options>

Ciò dovrebbe risolvere il problema e significa anche che non è necessario creare una funzione wrapper e sostituire tutte le occorrenze nel codice.


3
Brillante! Nessuna modifica al mio codice, nessun processo separato e funziona.
alexfernandez

ma vedrò ancora gli errori che ho fatto e otterrò l'output di error_reporting (E_ALL) ??
spankmaster79

1
@ spankmaster79 sì, andrà solo all'errore standard del tuo terminale. Per impostazione predefinita, la maggior parte dei terminali stamperà insieme lo standard out e lo standard error, ma in realtà sono flussi separati.
Jon Cairns

stavo diventando fatto !!! tnx per questo trucco, ha senso, gli errori dovrebbero essere segnalati allo stderror !!! TNX
th3n3rd

42
Puoi aggiungere il stderr="true"tuo phpunit.xml per salvare alcune sequenze di tasti.
tszming

9

Per inciso: per me headers_list()continuavo a restituire 0 elementi. Ho notato il commento di @titel sulla domanda e ho pensato che meritasse una menzione speciale qui:

Volevo solo coprire questo se ci sono altre persone interessate anche a questo. headers_list()non funziona durante l'esecuzione di PHPunit (che utilizza PHP CLI) ma xdebug_get_headers()funziona invece.

HTH


4

Come già accennato in un commento, penso che sia una soluzione migliore per definire processIsolation nel file di configurazione XML come

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

In questo modo, non devi passare l'opzione --stderr, che potrebbe irritare i tuoi colleghi.


4
Probabilmente è meglio definirlo solo per il test che lo richiede. Impostarlo per tutti i test renderà solo lenta l'esecuzione dei test.
Shi

3

Avevo una soluzione più radicale, da utilizzare $_SESSIONall'interno dei miei file testati / inclusi . Ho modificato uno dei file PHPUnit in ../PHPUnit/Utils/Printer.php per avere un "session_start();"prima del comando "print $ buffer" .

Ha funzionato per me come un fascino. Ma penso che la soluzione dell'utente "joonty" sia la migliore di tutte fino ad ora.


Hai inviato la richiesta pull nel repository? Immagino che questo potrebbe essere un problema comune.
t1gor

Grazie per il suggerimento, ho chiamato session_start () nel mio phpunit bootstrap.php e funziona per me
bumperbox

0

Una soluzione alternativa a @runInSeparateProcess consiste nello specificare l'opzione --process-isolation quando si esegue PHPUnit:

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

Questo è analogo all'impostazione dell'opzione processIsolation = "true" in phpunit.xml.

Questa soluzione ha vantaggi / svantaggi simili alla specifica dell'opzione --stderr, che tuttavia non ha funzionato nel mio caso. Fondamentalmente non sono necessarie modifiche al codice, anche se potrebbe esserci un calo delle prestazioni dovuto all'esecuzione di ogni test in un processo PHP separato.


Questo potrebbe essere un primo passo per rintracciare la causa e fare alcuni test, ma per creare test mantenibili, è sufficiente incorporare direttamente l'annotazione nel file di test. Minore è il numero di opzioni "speciali" richieste dalla riga di comando, più facile è la manutenzione di una configurazione del sistema CI.
Shi

chi ha mai effettuato il downgrade di #fail. questa risposta è corretta come un'altra opzione.
fb

0

Usa il parametro --stderr per ottenere le intestazioni da PHPUnit dopo i tuoi test.

phpunit --stderr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.