Przetestuj nagłówki PHP za pomocą PHPUnit

98

Próbuję użyć PHPunit do przetestowania klasy, która wyprowadza niektóre niestandardowe nagłówki.

Problem w tym, że na moim komputerze to:

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

a nawet to:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

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

        ob_clean();
    }
}

zwróć ten błąd:

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.

Wygląda na to, że przed uruchomieniem testu do terminala jest wysyłane coś innego, mimo że nie ma dołączonego innego pliku i nie ma innego znaku przed początkiem znacznika PHP. Czy przyczyną tego może być coś wewnątrz PHPunit?

Jaki może być problem?

tytuł
źródło
14
Chciałem to omówić, jeśli są też inne osoby zainteresowane tym. headers_list () nie działa podczas pracy z PHPunit (który używa PHP CLI), ale zamiast tego działa xdebug_get_headers ().
tytuł

Odpowiedzi:

124

Problem polega na tym, że PHPUnit wydrukuje nagłówek na ekranie iw tym momencie nie możesz dodać więcej nagłówków.

Rozwiązaniem jest uruchomienie testu w izolowanym procesie. Oto przykład

<?php

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

Spowoduje to:

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

Kluczem jest adnotacja @runInSeparateProcess.

Jeśli używasz PHPUnit ~ 4.1 lub czegoś podobnego i otrzymujesz błąd:

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

Spróbuj dodać to do pliku bootstrap, aby to naprawić:

<?php
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
    define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/path/to/composer/vendors/dir/autoload.php');
}
SamHennessy
źródło
7
powoduje to błędy w niektórych instrukcjach define () PHPUnit_Framework_Exception: Uwaga: Stała xyz już zdefiniowana
Minhaz
1
@mebjas To brzmi niepowiązane.
SamHennessy
4
Otrzymałem to, gdy 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
złożyłem
2
Jest to zdecydowanie najlepsza opcja rozwiązania problemu. Zadziałało jak urok!
xarlymg89
2
Musiałem użyć xdebug_get_headers (), aby uzyskać tablicę ustawionych nagłówków. Funkcja globalna headers_list () nie działała w moim przypadku.
Shalom Sam
108

Chociaż uruchomienie testu w oddzielnym procesie rozwiązuje problem, podczas uruchamiania dużego zestawu testów występuje zauważalny narzut.

Moją poprawką było skierowanie wyjścia phpunita na stderr, na przykład:

phpunit --stderr <options>

To powinno rozwiązać problem, a także oznacza, że ​​nie musisz tworzyć funkcji opakowującej i zastępować wszystkich wystąpień w kodzie.

Jon Cairns
źródło
3
Znakomity! Żadnych zmian w moim kodzie, żadnych oddzielnych procesów i to działa.
alexfernandez
ale czy nadal będę widzieć błędy, które popełniłem i otrzymam wyjście error_reporting (E_ALL)?
spankmaster79
1
@ spankmaster79 tak, przejdzie do standardowego błędu twojego terminala. Domyślnie większość terminali drukuje razem standardowe wyjście i standardowy błąd, ale w rzeczywistości są to oddzielne strumienie.
Jon Cairns
stawałem się stworzony !!! tnx dla tej sztuczki, ma sens, błędy powinny być zgłaszane do standardowego błędu !!! Tnx
th3n3rd
42
Możesz dodać stderr="true"plik phpunit.xml, aby zaoszczędzić kilka naciśnięć klawiszy.
tszming
10

Na marginesie: dla mnie headers_list()zwracało 0 elementów. Zauważyłem komentarz @titel do pytania i doszedłem do wniosku, że zasługuje na specjalną wzmiankę:

Chciałem to omówić, jeśli są też inne osoby zainteresowane tym. headers_list()nie działa podczas uruchamiania PHPunit (który używa PHP CLI), ale xdebug_get_headers()działa zamiast tego.

HTH

Melle
źródło
4

Jak już wspomniano w komentarzu, myślę, że lepszym rozwiązaniem jest zdefiniowanie processIsolation w pliku konfiguracyjnym XML, np

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

W ten sposób nie musisz przechodzić opcji --stderr, która może irytować twoich współpracowników.

PepeNietnagel
źródło
4
Prawdopodobnie lepiej jest zdefiniować to tylko dla testu, który tego wymaga. Ustawienie go dla wszystkich testów po prostu spowolni wykonywanie testów.
Shi
3

Miałem bardziej radykalne rozwiązanie, aby używać $_SESSIONwewnątrz moich testowanych / dołączonych plików. Edytowałem jeden z plików PHPUnit pod adresem ../PHPUnit/Utils/Printer.php, aby "session_start();"przed poleceniem „print $ buffer” znajdował się znak „print $ buffer” .

U mnie zadziałało jak urok. Ale myślę, że rozwiązanie użytkownika "joonty" jest najlepsze z dotychczasowych.

Sergio Abreu
źródło
Czy wysłałeś żądanie ściągnięcia w repozytorium? Myślę, że to może być powszechny problem.
t1gor
Dzięki za podpowiedź, zadzwoniłem do session_start () w moim phpunit bootstrap.php i to działa dla mnie
bumperbox
0

Alternatywnym rozwiązaniem dla @runInSeparateProcess jest określenie opcji --process-isolation podczas uruchamiania PHPUnit:

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

Jest to analogiczne do ustawienia opcji processIsolation = "true" w phpunit.xml.

To rozwiązanie ma podobne zalety / wady jak podanie opcji --stderr, która jednak nie zadziałała w moim przypadku. Zasadniczo nie są konieczne żadne zmiany kodu, nawet jeśli może wystąpić spadek wydajności z powodu uruchamiania każdego testu w oddzielnym procesie PHP.

AlexB
źródło
Może to być pierwszy krok w wyśledzeniu przyczyny i wykonaniu niektórych testów, ale aby stworzyć możliwe do utrzymania testy, wystarczy bezpośrednio osadzić adnotację w pliku testowym. Im mniej opcji „specjalnych” jest wymaganych z wiersza poleceń, tym łatwiejsze jest utrzymanie konfiguracji systemu CI.
Shi
kto kiedykolwiek zdegradował #fail. ta odpowiedź jest poprawna jako inna opcja.
fb
0

Użyj parametru --stderr, aby pobrać nagłówki z PHPUnit po przeprowadzeniu testów.

phpunit --stderr
NSukonny
źródło