Jak wyprowadzać w CLI podczas wykonywania testów jednostkowych PHP?

151

Podczas uruchamiania testu PHPUnit chciałbym mieć możliwość zrzucenia danych wyjściowych, aby móc debugować jedną lub dwie rzeczy.

Wypróbowałem następujące (podobne do przykładu z podręcznikiem PHPUnit );

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

Z następującym wynikiem:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Zauważ, że nie ma żadnego oczekiwanego wyniku.

Używam wersji HEAD repozytoriów git z 19 września 2011 r.

Wyjście php -version:

$ php -version
PHP 5.2.9 (cli) (built: Dec  8 2010 11:36:37) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
    with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans

Czy jest coś, co robię źle, czy jest to potencjalnie błąd PHPUnit?

Jess Telford
źródło
1
Gdzie jest kod, który wywołuje testOutput()metodę?
Derrick Tucker,
Próbujesz naprawdę desperacko (echo, print, print_r, var_dump - to właściwie wszystko "wyjście"), normalnie nie mam problemu z wykonaniem wyników testów. Możesz sprawdzić, czy buforowanie wyjścia jest włączone: php.net/manual/en/function.ob-get-level.php - A tak przy okazji, najbezpieczniejszym sposobem na wymuszenie „testowania” jest zgłoszenie wyjątku.
hakre
3
@DerrickTucker PHPUnit robi to przez wywołanie phpunit /path/to/tests/theTest.php(jeśli powyższa klasa była w pliku theTest.php).
Jess Telford,
@hakre ob_get_level()powraca 1. Jest to jednak sprzeczne z następującym kodem: while (ob_get_level() > 0) { ob_end_flush(); }which errors with ob_end_clean(): failed to delete buffer. No buffer to delete.. Ciekawsze i ciekawsze.
Jess Telford
1
Mówi się, że to kod phpunita powoduje błąd - oczywiście dlatego, że połykanie wyjścia phpunita jest aktywne (ale go zepsułeś). Dokładnie, nazwa funkcji też się różni.
hakre

Odpowiedzi:

196

AKTUALIZACJA

Właśnie zdałem sobie sprawę z innego sposobu na zrobienie tego, który działa znacznie lepiej niż --verboseopcja wiersza poleceń:

class TestSomething extends PHPUnit_Framework_TestCase {
    function testSomething() {
        $myDebugVar = array(1, 2, 3);
        fwrite(STDERR, print_r($myDebugVar, TRUE));
    }
}

Pozwala to w dowolnym momencie zrzucić wszystko na konsolę bez wszystkich niechcianych danych wyjściowych, które są dostarczane wraz z --verboseopcją CLI.


Jak zauważyły ​​inne odpowiedzi, najlepiej przetestować dane wyjściowe za pomocą wbudowanych metod, takich jak:

$this->expectOutputString('foo');

Czasami jednak warto być niegrzecznym i zobaczyć jednorazowe / tymczasowe wyniki debugowania z przypadków testowych. Nie ma jednak potrzeby var_dumphakowania / obejścia. Można to łatwo osiągnąć, ustawiając --verboseopcję wiersza poleceń podczas uruchamiania zestawu testów. Na przykład:

$ phpunit --verbose -c phpunit.xml

Spowoduje to wyświetlenie danych wyjściowych z wnętrza metod testowych podczas uruchamiania w środowisku CLI.

Zobacz: Pisanie testów dla PHPUnit - testowanie wyników .

rdlowrey
źródło
5
przepraszam, przegapiliśmy pisanie do stderr. Rzeczywiście działa. Po prostu byłem zmuszony użyć file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);zamiast tego, ponieważ miałem wiadomość Use of undefined constant STDERR - assumed 'STDERR'z fwrite .
Serge
Problem polega na tym, że nie wydaje się to działać z izolacją procesów.
donquixote
@donquixote nie dziwi, ponieważ test zostanie wykonany w innym procesie, którego STDERR wyjściowy strumień jest prawdopodobnie odrzucony ...
rdlowrey
1
Możesz również użyć STDOUTzamiastSTERR
Chris
2
Tak. Działa i wydaje się działać tak samo, jak STDERR. Używam PHPUnit 4.5.0w linii cmd systemu Windows. echooświadczenie nie daje te same rezultaty. echowyprowadza dane, ale dopiero po wyświetleniu wyniku testu. fwrite(STDERR, 'string')lub fwrite(STDOUT,'string')dają takie same wyniki: dane wyjściowe przed wyświetleniem wyniku testu.
Chris,
33

Aktualizacja: Zobacz aktualizację rdlowrey poniżej dotyczącą użycia fwrite(STDERR, print_r($myDebugVar, TRUE));jako znacznie prostszego obejścia


Takie zachowanie jest zamierzone (jak jasonbar nie zauważył ). Stan instrukcji w sprzeczności został zgłoszony do PHPUnit.

Rozwiązaniem jest zapewnienie PHPUnit, że oczekiwane wyjście jest puste (gdy w rzeczywistości jest wyjście), co spowoduje wyświetlenie nieoczekiwanego wyniku.

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        $this->expectOutputString(''); // tell PHPUnit to expect '' as output
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

daje:

PHPUnit @package_version@ by Sebastian Bergmann.

F

Time: 1 second, Memory: 3.50Mb

There was 1 failure:

1) theTest::testOutput
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-''
+'Hello WorldPingPongstring(4) "Foo"
+'

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Pamiętaj, aby wyłączyć wszystkie inne asercje, które masz do testu, ponieważ mogą się nie powieść przed przetestowaniem potwierdzenia wyjściowego (i dlatego nie zobaczysz wyniku).

Jess Telford
źródło
33

Spróbuj użyć --debug

Przydatne, jeśli próbujesz uzyskać właściwą ścieżkę do dołączonego lub źródłowego pliku danych.

chim
źródło
2
To jest dla mnie poprawna odpowiedź. Wszystkie wypowiedzi napisane we wcześniejszych odpowiedziach nie działały na mnie.
Kim Stacks
9

To nie jest błąd, ale bardzo celowy. Najlepszym rozwiązaniem jest zapisanie w jakimś pliku dziennika i śledzenie dziennika w poszukiwaniu wyników.

Jeśli próbujesz przetestować dane wyjściowe, sprawdź to .

Również:

Uwaga : należy pamiętać, że PHPUnit połyka wszystkie dane wyjściowe, które są emitowane podczas wykonywania testu. W trybie ścisłym test, który emituje dane wyjściowe, zakończy się niepowodzeniem.

jasonbar
źródło
1
Gdyby to było zamierzone, to z pewnością instrukcja nie dawałaby tego przykładu ? Nie próbuj też testować samego wyjścia. Używając go po prostu do przyjrzenia się niektórym wynikom, które powodują niepowodzenie testów, gdy nie powinny.
Jess Telford
Jak napisano: normalnie nie mam problemu z wyświetlaniem echa podczas uruchamiania testów. Być może masz konfigurację, która przechwytuje dane wejściowe.
hakre
1
Gdyby nie było to zamierzone, to z pewnością instrukcja nie mówi, że tak .
jasonbar,
1
Wygląda więc na konflikt w dokumentacji. @hakre wydaje się mieć takie samo wrażenie jak ja (że nie należy go połykać) - która część dokumentacji jest poprawna?
Jess Telford
Testy generowania danych wyjściowych kończą się niepowodzeniem, gdy --disallow-test-output (lub plik conf ma beStrictAboutOutputDuringTests = "true") - dokumentacja mówi teraz: „Test, który emituje dane wyjściowe, na przykład przez wywołanie print w kodzie testu lub w testowanym kodzie, zostanie oznaczony jako ryzykowny, gdy ta kontrola jest włączona. " phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
wskaźnik NULL
7

Mam trochę szczęścia z VisualPHPUnit i między innymi pomaga on w wyświetlaniu wyników.

class TestHello extends PHPUnit_Framework_TestCase 
{
    public function test_Hello() 
    {
        print "hello world";
    }
}

Wyniki TestHello

Bob Stein
źródło
Hmm, dlaczego głos przeciw? Dlaczego nie jest to pomocne jako alternatywny sposób zrzutu danych debugowania w teście PHPUnit?
Bob Stein
1
Zgaduję, że to jest dyskutowane, ponieważ jeśli ktoś spróbuje to uruchomić, pojawi się błąd składni. Ogromny.
Jimbo
Zapomniałem o funkcji. Teraz jest naprawiony, przetestowany, wycięty i wklejony. Dzięki, @Jimbo
Bob Stein
Niestety w tej chwili nie jest kompatybilny z PHP 7, najwyraźniej: "VisualPHPUnit nie jest obecnie kompatybilny z php 7 ze względu na sposób wykorzystania phpunita. PHP 7 będzie obsługiwane w następnej głównej wersji"
leo
6

Powinieneś naprawdę pomyśleć o swoich zamiarach: jeśli potrzebujesz informacji teraz podczas debugowania, aby naprawić test, będziesz potrzebować ich ponownie w przyszłym tygodniu, gdy testy się zakończą.

Oznacza to, że będziesz potrzebować informacji zawsze, gdy test się nie powiedzie - i dodasz plikvar_dump celu znalezienia przyczyny to po prostu zbyt dużo pracy. Zamiast tego umieść dane w swoich twierdzeniach.

Jeśli twój kod jest na to zbyt złożony, podziel go, aż osiągniesz poziom, na którym jedno potwierdzenie (z niestandardową wiadomością) powie ci wystarczająco dużo, aby wiedzieć, gdzie się zepsuł, dlaczego i jak naprawić kod.

cweiske
źródło
1
W 100% zgadzam się ze wszystkim, co powiedziałeś. Używam PHPUnit do wykonywania testów integracji, które ostatecznie wywołują jeden z interfejsów API XML firmy Google. Wszystkie testy jednostkowe przeszły pomyślnie (z symulowanymi wywołaniami interfejsu API), ale test końcowy (z wywołaniami interfejsu API na żywo) nie powiódł się. Okazało się, że to wina Google API, ale w międzyczasie chciałem zrzucić surową odpowiedź HTTP.
Jess Telford
2
A co, jeśli musisz debugować swój kod na drodze do osiągnięcia tego, co tutaj opisałeś?
David Meister
2
Dlatego nie lubię odpowiedzi, które w drugiej kolejności odgadują, czego chcą użytkownicy. Jestem tutaj, ponieważ mam test, który czeka na wyczyszczenie pamięci podręcznej. Przy 5-sekundowych ttl cache oznacza to, że mój test zawiesza się na ~ 16 sekund. Chciałbym tylko powiadomić użytkownika, że ​​nie, nic się nie dzieje, po prostu czekamy, aż upłynie limit czasu pamięci podręcznej. Gdyby ludzie potrafili po prostu odpowiedzieć na pytanie, to osoby z innymi przypadkami użycia również miałyby swoją odpowiedź.
user151841
4

W laravel 5 możesz użyć dump (), Zrzuć zawartość ostatniej odpowiedzi.

class ExampleTest extends TestCase{
    public function test1()
    {
        $this->post('/user', ['name' => 'Gema']);
        $this->dump();
    }
}

daje

Branny Bk
źródło
4

Po prostu użyj flagi --verbose podczas uruchamiania phpunita .

$ phpunit --verbose -c phpunit.xml 

Zaletą tej metody jest to, że nie musisz zmieniać kodu testu, możesz zawsze drukować łańcuchy, var_dump lub cokolwiek zechcesz, a zostanie to pokazane w konsoli tylko wtedy, gdy ustawiony jest tryb gadatliwy .

Mam nadzieję, że to pomoże.

Fabricio
źródło
3

W niektórych przypadkach można by użyć czegoś takiego do wyprowadzenia czegoś na konsolę

class yourTests extends PHPUnit_Framework_TestCase
{
    /* Add Warnings */
    protected function addWarning($msg, Exception $previous = null)
    {
        $add_warning = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_Warning($msg, 0, $previous);
        $add_warning->addWarning($this, $msg, time());
        $this->setTestResultObject($add_warning);
    }

    /* Add errors */
    protected function addError($msg, Exception $previous = null)
    {
        $add_error = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_error->addError($this, $msg, time());
        $this->setTestResultObject($add_error);
    }

    /* Add failures */
    protected function addFailure($msg, Exception $previous = null)
    {
        $add_failure = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_failure->addFailure($this, $msg, time());
        $this->setTestResultObject($add_failure);
    }

    public function test_messages()
    {
        $this->addWarning("Your warning message!");
        $this->addError("Your error message!");
        $this->addFailure("Your Failure message");
    }

    /* Or just mark test states! */
    public function test_testMarking()
    {
        $this->markTestIncomplete();
        $this->markTestSkipped();
    }
}
mkungla
źródło
3

Hackish, but works: wyrzuć wyjątek z wyjściem debugowania jako komunikatem.

class theTest extends PHPUnit_Framework_TestCase
{
    public function testOutput() {
        throw new \Exception("hello");
    }   
}

Plony:

...
There was 1 error:

1) theTest::testOutput
Exception: hello
Matthias Rella
źródło
2

To pochodzi z PHPUnit Docs about Fixtures .

Powinno to pozwolić na zrzucenie informacji w dowolnym momencie podczas cyklu życia testu phpunit.

Po prostu zastąp __METHOD__w poniższym kodzie tym, co chcesz wyprowadzić

Przykład 4.2: Przykład pokazujący wszystkie dostępne metody szablonów

<?php
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
    public static function setUpBeforeClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function setUp()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function assertPreConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function testOne()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(TRUE);
    }

    public function testTwo()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(FALSE);
    }

    protected function assertPostConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function tearDown()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public static function tearDownAfterClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function onNotSuccessfulTest(Exception $e)
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        throw $e;
    }
}
?>
Chris
źródło
1

Wyprowadzam mój Testresults w formacie HTML, w tym przypadku pomocne było opróżnienie zawartości:

var_dump($array);
ob_flush();

Istnieje druga metoda PHP

flush() 

którego nie próbowałem.

Sudo
źródło
1

PHPUnit ukrywa dane wyjściowe za pomocą ob_start(). Możemy to tymczasowo wyłączyć.

    public function log($something = null)
    {
        ob_end_clean();
        var_dump($something);
        ob_start();
    }
Sława
źródło
0

Musiałem zmodyfikować kod źródłowy, aby ten kod działał, więc musisz dodać URL dla tego rozwidlonego repozytorium do kompozytora, aby to zadziałało

class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     *  Save last response
     * @var Response|null A Response instance
     */
    static $lastResponse;
    /**
     *  Modify to save response
     *
     * @param  string $method
     * @param  string $uri
     * @param  array $parameters
     * @param  array $files
     * @param  array $server
     * @param  string $content
     * @param  bool $changeHistory
     * @return \Illuminate\Http\Response
     */
    final public function call(
        $method,
        $uri,
        $parameters = [],
        $files = [],
        $server = [],
        $content = null,
        $changeHistory = true
    ) {

        $response = parent::call($method, $uri, $parameters, $files, $server, $content, $changeHistory);
        static::$lastResponse = $this->client->getResponse();
        return $response;
    }


    /**
     * Modify message to add response text
     *
     * @param mixed $value
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string $message
     * @since  Method available since Release 3.0.0
     */
    final public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
    {
        $message .= PHP_EOL . static::$lastResponse . PHP_EOL;
        parent::assertThat($value, $constraint, $message);
    }
}
Gadelkareem
źródło
0

Oto kilka metod przydatnych do drukowania komunikatów debugowania w PHPUnit 4.x:

  • syslog(LOG_DEBUG, "Debug: Message 1!");

    Bardziej praktyczny przykład:

    syslog(LOG_DEBUG, sprintf("%s: Value: %s", __METHOD__, var_export($_GET, TRUE)));

    Wywołanie syslog()wygeneruje komunikat dziennika systemowego (patrz man syslog.conf:).

    Uwaga: Możliwe poziomy: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, itd.

    W systemie macOS, aby przesyłać strumieniowo wiadomości syslog w czasie rzeczywistym, uruchom:

    log stream --level debug --predicate 'processImagePath contains "php"'
  • fwrite(STDERR, "LOG: Message 2!\n");

    Uwaga: STDERRStała nie jest dostępna, jeśli czytasz skrypt PHP ze standardowego wejścia . Oto obejście .

    Uwaga: Zamiast tego STDERRmożesz również określić nazwę pliku.

  • file_put_contents('php://stderr', "LOG: Message 3!\n", FILE_APPEND);

    Uwaga: użyj tej metody, jeśli nie masz zdefiniowanej STDERRstałej .

  • register_shutdown_function('file_put_contents', 'php://stderr', "LOG: Message 4!\n", FILE_APPEND);

    Uwaga: użyj tej metody, jeśli chcesz wydrukować coś na samym końcu bez wpływu na testy.

Aby zrzucić zmienną, użyj var_export()np "Value: " . var_export($some_var, TRUE) . "\n".

Aby wydrukować powyższe wiadomości tylko w trybie szczegółowym lub debugowania, zobacz: Czy istnieje sposób na sprawdzenie, czy --debug lub --verbose zostały przekazane do PHPUnit w teście?


Chociaż jeśli testowanie danych wyjściowych jest częścią testu samoczynnego, zobacz: stronę z dokumentacją Testowanie danych wyjściowych .

kenorb
źródło
-1

Jeśli używasz Laravel, możesz użyć funkcji logowania, takich jak info (), aby zalogować się do pliku dziennika Laravel w obszarze storage / logs. Więc nie pojawi się w twoim terminalu, ale w pliku dziennika.

Lew
źródło