Як вивести в 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 repos від 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()метод?
Деррік Такер

Ви дуже відчайдушно намагаєтесь (ехо, print, print_r, var_dump - це в основному все "вихід"), зазвичай у мене немає проблем з результатами тестів. Ви можете перевірити, чи включена вихідна буферизація: php.net/manual/en/function.ob-get-level.php - І найбезпечніший спосіб насильно "перевірити" - це кинути виняток BTW.
хакре

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.. Curiouser і curiouser.
Джесс Телфорд

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у вікні 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
Це правильна відповідь для мене. Усі заяви, написані в попередніх відповідях, не працювали для мене.
Кім Стек

9

Це не помилка, але дуже навмисно. Ваша найкраща ставка - записати в якийсь файл журналу та передати хвіст журналу, щоб слідкувати за результатами.

Якщо ви намагаєтесь вивести TEST, перевірте це .

Також:

Примітка . Зверніть увагу: PHPUnit проковтує весь результат, який випромінюється під час виконання тесту. У суворому режимі тест, який випромінює вихід, не вдасться.


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

Як написано: У мене зазвичай не виникає проблем, коли лунає тест. Можливо, у вас є якась конфігурація, яка збирає дані.
хакре

1
Якби це не було навмисно, то, звичайно, посібник не сказав би , що це було .
jasonbar

1
Тож здається конфлікт у документації. @hakre, здається, під таким же враженням, що і я (що його не слід ковтати) - яка частина документації правильна?
Джесс Телфорд

Тести, що генерують вихід, ТІЛЬКИ виходять з ладу, коли --disallow-test-output (або конф-файл має beStrict AboutOutputDuringTests = "true") - в документації тепер написано "Тест, який видає вихід, наприклад, викликаючи друк або в тестовому коді, або в тестованому коді, буде позначено як ризикове, коли ця перевірка включена. " 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 API) не вдався. Виявилося, що це помилка API API, але в той же час я хотів скинути сиру HTTP-відповідь.
Джесс Телфорд

2
Що робити, якщо вам потрібно налагодити код на шляху до досягнення того, що ви тут намітили?
Девід Мейстер

2
Ось чому мені не подобаються відповіді, які вдруге здогадуються, що хочуть робити користувачі. Я тут, бо в мене є тест, який чекає, коли кеш очиститься. З 5 секунд кеш-пам'яті, це означає, що мій тест зависає ~ 16 секунд. Я просто хотів би повідомити користувачеві про те, що ні, нічого поганого, ми просто чекаємо, коли тайм-аут вимкнеться. Якщо люди можуть просто відповісти на питання, то люди, які мають інші випадки використання, також отримали б свою відповідь.
користувач151841

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

Я виводжу свій тестрезультат на базі 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 для потокового передавання повідомлень syslog в режимі реального часу запустіть:

    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, ви можете використовувати такі функції реєстрації, як інформація (), щоб увійти до файлу журналу Laravel під зберіганням / журналами. Таким чином він відображатиметься не у вашому терміналі, а у файлі журналу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.