Как вывести в CLI во время выполнения модульных тестов PHP?


151

При запуске теста PHPUnit я хотел бы иметь возможность выводить выходные данные, чтобы я мог отлаживать одну или две вещи.

Я попробовал следующее (аналогично примеру с руководством по 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);
    }   
}

Со следующим результатом:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Обратите внимание, что нет ожидаемого результата.

Я использую HEAD версии репозитория git по состоянию на 19 сентября 2011 года.

Выход 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

Есть ли что-то, что я делаю не так, или это потенциально ошибка PHPUnit?


1
Где код, который вызывает testOutput()метод?
Деррик Такер

Вы действительно отчаянно пытаетесь (echo, print, print_r, var_dump - это в основном все «выходные данные»), обычно у меня нет проблем с выводом результатов тестов. Вы можете проверить, включена ли буферизация вывода: php.net/manual/en/function.ob-get-level.php - И самый безопасный способ принудительного «тестирования» - это выбросить исключение BTW.
hakre

3
@DerrickTucker PHPUnit делает это путем вызова phpunit /path/to/tests/theTest.php(если вышеупомянутый класс был в файле theTest.php).
Джесс Телфорд

@hakre ob_get_level()возвращается 1. Однако это противоречит следующему коду: while (ob_get_level() > 0) { ob_end_flush(); }какие ошибки с ob_end_clean(): failed to delete buffer. No buffer to delete.. Любопытнее и любопытнее.
Джесс Телфорд

1
Это говорит о том, что код phpunit вызывает ошибку - очевидно, потому что глотание вывода phpunits активно (но вы его сломали). Посмотрите точно, имя функции также отличается.
хакре

Ответы:


196

ОБНОВИТЬ

Только что реализовал другой способ сделать это, который работает намного лучше, чем --verboseопция командной строки:

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

Это позволяет в любое время вывести на консоль что-нибудь, не используя нежелательный вывод, который поставляется вместе с --verboseопцией CLI.


Как отмечали другие ответы, лучше всего тестировать вывод, используя встроенные методы, такие как:

$this->expectOutputString('foo');

Однако иногда полезно быть непослушным и видеть одноразовые / временные выходные данные отладки в ваших тестовых примерах. Тем не var_dumpменее, нет необходимости во взломе / обходном пути. Это можно легко сделать, установив--verbose командной строки при запуске набора тестов. Например:

$ phpunit --verbose -c phpunit.xml

Это будет отображать вывод из ваших методов тестирования при работе в среде CLI.

См .: Написание тестов для PHPUnit - Тестирование вывода .


5
извините, пропустил мы пишем в stderr. Действительно работает. Я просто был вынужден использовать file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);вместо этого, потому что у меня было сообщение Use of undefined constant STDERR - assumed 'STDERR'с fwrite .
Серж

Проблема в том, что это, похоже, не работает с изоляцией процесса.
Donquixote

@donquixote Не удивительно, поскольку тест будет выполнен в другом процессе, чей вывод потока STDERR, вероятно, отбрасывается ...
rdlowrey

1
Вы можете также использовать STDOUTвместоSTERR
Крис

2
Да. Это работает и, кажется, выводить так же, как STDERR. Я использую PHPUnit 4.5.0в Windows CMD линии. echoзаявление не дает те же результаты. echoвыводит, но только после того, как отображается результат теста. fwrite(STDERR, 'string')или fwrite(STDOUT,'string')выдает те же результаты: вывод перед отображением результата теста.
Крис

33

Обновление: см. Обновление rdlowrey ниже относительно использования fwrite(STDERR, print_r($myDebugVar, TRUE));как намного более простого обходного пути.


Такое поведение является намеренным (как jasonbar уже указывал ). О конфликтующем состоянии руководства сообщили в PHPUnit.

Обходной путь - заставить PHPUnit утверждать, что ожидаемый вывод является пустым (при наличии фактического вывода), что приведет к отображению неожиданного вывода.

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

дает:

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.

Обязательно отключите любые другие утверждения, которые у вас есть для теста, поскольку они могут потерпеть неудачу до того, как будет проверено выходное утверждение (и, следовательно, вы не увидите выходных данных).


33

Попробуйте использовать --debug

Полезно, если вы пытаетесь найти правильный путь к файлу данных включения или источника.


2
Это правильный ответ для меня. Все высказывания fwrite, написанные в предыдущих ответах, не сработали для меня.
Ким Стеки

9

Это не ошибка, но очень намеренно. Лучше всего записать в какой-нибудь файл журнала и привязать журнал, чтобы отслеживать результаты.

Если вы пытаетесь проверить вывод, проверьте это .

Также:

Примечание . Обратите внимание, что PHPUnit поглощает все выходные данные, которые генерируются во время выполнения теста. В строгом режиме тест, выдающий выходные данные, не будет выполнен.


1
Если бы это было преднамеренно, то, конечно, руководство не дало бы пример этого ? Кроме того, не пытаясь проверить сам вывод. Просто используйте его, чтобы увидеть некоторые результаты, в результате чего тесты не пройдут, если они не должны.
Джесс Телфорд

Как написано: у меня обычно нет проблем с выводом при запуске тестов. У вас может быть какая-то конфигурация, которая ловит ввод.
Хакре

1
Если это не было преднамеренным, то, конечно, руководство не скажет, что это было .
Джейсонбар

1
Так что, похоже, конфликт в документации. У @hakre, похоже, такое же впечатление, как у меня (что его не следует проглатывать) - какая часть документации верна?
Джесс Телфорд

Тесты, генерирующие выходные данные, ТОЛЬКО терпят неудачу, когда --disallow-test-output (или файл conf имеет beStrictAboutOutputDuringTests = "true") - теперь в документации говорится: "Тест, который генерирует выходные данные, например, вызывая print либо в коде теста, либо в тестированном коде, будет помечен как рискованный, когда эта проверка включена. " phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
указатель NULL

7

Мне повезло с VisualPHPUnit , и он, помимо всего прочего, показывает полезную информацию .

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

Результаты TestHello


Хм, почему понизить? Как это не полезно в качестве альтернативного способа вывести отладочный вывод в тесте PHPUnit?
Боб Стейн

1
Я предполагаю, что это недооценено, потому что, если кто-то попытается выполнить это, вы получите синтаксическую ошибку. Массивный.
Джимбо

Я забыл функцию. Теперь это исправлено, проверено, вырезано и приклеено. Спасибо, @Jimbo
Боб Стейн

К сожалению, на данный момент он не совместим с PHP 7, по-видимому: «VisualPHPUnit не поддерживает php 7 в настоящее время из-за способа использования phpunit. Php 7 будет поддерживаться в следующем основном выпуске»
Лев

6

Вы должны действительно подумать о своих намерениях: если вам нужна информация сейчас при отладке для исправления теста, она понадобится вам на следующей неделе, когда тесты прервутся.

Это означает, что вам понадобится информация всегда, когда тест не пройден, и добавление a var_dumpдля поиска причины - это слишком много работы. Скорее поместите данные в свои утверждения.

Если ваш код слишком сложен для этого, разбивайте его до тех пор, пока не достигнете уровня, когда одно утверждение (с пользовательским сообщением) скажет вам достаточно, чтобы знать, где оно сломалось, почему и как исправить код.


1
Я согласен на 100% со всем, что вы сказали. Я использую PHPUnit для проведения интеграционных тестов, которые в конечном итоге вызывают один из XML-API Google. Все модульные тесты пройдены (с поддельными вызовами API), но окончательный тест (с активными вызовами API) не прошел. Оказалось, что это была ошибка Google API, но в то же время я хотел выбросить необработанный HTTP-ответ.
Джесс Телфорд

2
Что если вам нужно отладить свой код на пути к достижению того, что вы здесь изложили?
Дэвид Мейстер

2
Вот почему мне не нравятся ответы на второй вопрос, что пользователи хотят делать. Я здесь, потому что у меня есть тест, который ждет очистки кеша. С 5 секундным кешем ttls это означает, что мой тест зависает на ~ 16 секунд. Я просто хотел бы уведомить пользователя о том, что нет, ничего страшного, мы просто ждем тайм-аутов кешей. Если люди могут просто ответить на вопрос, то и у людей с другими вариантами использования тоже будет свой ответ.
user151841

4

В laravel 5 вы можете использовать dump (), дамп содержимого из последнего ответа.

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

дает


4

Просто используйте флаг --verbose при выполнении phpunit .

$ phpunit --verbose -c phpunit.xml 

Преимущество этого метода заключается в том, что вам не нужно менять тестовый код, вы можете печатать строки, var_dump или все, что пожелаете, и это будет отображаться в консоли только при установленном подробном режиме.

Надеюсь, это поможет.


3

В некоторых случаях можно использовать что-то подобное для вывода чего-либо на консоль.

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

3

Взломать, но работает: сгенерировать исключение с выводом отладки в качестве сообщения.

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

Урожайность:

...
There was 1 error:

1) theTest::testOutput
Exception: hello

2

Это было взято из PHPUnit Docs о приспособлениях .

Это должно позволить вам выводить информацию в любой точке в течение жизненного цикла теста phpunit.

Просто замените __METHOD__в приведенном ниже коде все, что вы хотите вывести

Пример 4.2: Пример, показывающий все доступные методы шаблона

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

1

Я выводил свой Testresults на основе HTML, в этом случае было полезно очистить содержимое:

var_dump($array);
ob_flush();

Есть второй метод PHP

flush() 

который я не пробовал.


1

PHPUnit скрывает вывод ob_start(). Мы можем временно отключить это.

    public function log($something = null)
    {
        ob_end_clean();
        var_dump($something);
        ob_start();
    }

0

Мне пришлось изменить исходный код, чтобы этот код работал, поэтому вам нужно добавить URL для этого разветвленного репозитория, чтобы композитор работал.

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

0

Вот несколько методов, полезных для печати отладочных сообщений в PHPUnit 4.x:

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

    Более практичный пример:

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

    При вызове syslog()будет сгенерировано сообщение системного журнала (см . man syslog.conf:).

    Примечание: Возможные уровни: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING,LOG_ERR и т.д.

    В macOS для потоковой передачи сообщений системного журнала в реальном времени выполните:

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

    Примечание. STDERRКонстанта недоступна при чтении сценария PHP из stdin . Вот обходной путь .

    Примечание. Вместо этого STDERRвы также можете указать имя файла.

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

    Примечание. Используйте этот метод, если у вас нет определенной STDERRконстанты .

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

    Примечание. Используйте этот метод, если вы хотите напечатать что-либо в самом конце, не затрагивая тесты.

Чтобы вывести переменную, используйте var_export(), например "Value: " . var_export($some_var, TRUE) . "\n".

Чтобы напечатать вышеприведенные сообщения только в режиме подробного или отладочного режима, см .: Есть ли способ узнать, были ли --debug или --verbose переданы PHPUnit в тесте?


Хотя, если тестирование выходных данных является частью самого теста, ознакомьтесь со страницей : Тестирование выходных документов.


-1

Если вы используете Laravel, то вы можете использовать функции ведения журнала, такие как info (), чтобы войти в файл журнала Laravel в разделе storage / logs. Таким образом, он не появится в вашем терминале, но в файле журнала.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.