Есть ли способ вывода STDERR в PHPUnit?

У меня есть класс, который выводит на STDERR, но мне трудно найти способ заставить PHPUnit протестировать его вывод.

Класс PHPUnit_Extensions_OutputTestCase также не работал.

Ответ 1

Я не вижу способ буферизации stderr, как вы можете, с помощью stdout, поэтому я бы реорганизовал ваш класс, чтобы переместить вызов, который делает фактический вывод новым методом. Это позволит вам издеваться над этим методом во время тестирования, чтобы проверить выход или подкласс с буферами.

Например, скажем, у вас есть класс, который перечисляет файлы в каталоге.

class DirLister {
    public function list($path) {
        foreach (scandir($path) as $file) {
            echo $file . "\n";
        }
    }
}

Сначала извлеките вызов echo. Сделайте его защищенным, чтобы вы могли переопределить и/или высмеять его.

class DirLister {
    public function list($path) {
        foreach (scandir($path) as $file) {
            $this->output($file . "\n");
        }
    }

    protected function output($text) {
        echo $text ;
    }
}

Во-вторых, либо издевайтесь, либо подклассифицируйте его в своем тесте. Издеваться легко, если у вас есть простой тест или вы не ожидаете много звонков на output. Подкласс для буферизации вывода проще, если вы проверяете большой объем вывода.

class DirListTest extends PHPUnit_Framework_TestCase {
    public function testHomeDir() {
        $list = $this->getMock('DirList', array('output'));
        $list->expects($this->at(0))->method('output')->with("a\n");
        $list->expects($this->at(1))->method('output')->with("b\n");
        $list->expects($this->at(2))->method('output')->with("c\n");
        $list->list('_files/DirList'); // contains files 'a', 'b', and 'c'
    }
}

Переопределение output для буферизации всех $text во внутренний буфер остается как упражнение для читателя.

Ответ 2

Вы не можете перехватить и fwrite(STDERR); из тестового примера с помощью phpunit. В этом случае вы даже не можете перехватить fwrite(STDOUT);, даже не с буферизацией вывода.

Поскольку я предполагаю, что вы действительно не хотите вводить STDERR в свой "errorOutputWriter" (так как это не имеет никакого смысла для класса писать где-то еще), это один из немногих случаев, когда Я бы предложил такой небольшой взлом:

<?php 

class errorStreamWriterTest extends PHPUnit_Framework_TestCase {

    public function setUp() {
        $this->writer = new errorStreamWriter();
        $streamProp = new ReflectionProperty($this->writer, 'errorStream');
        $this->stream = fopen('php://memory', 'rw');
        $streamProp->setAccessible(true);
        $streamProp->setValue($this->writer, $this->stream);
    }

    public function testLog() {
        $this->writer->log("myMessage");
        fseek($this->stream, 0);
        $this->assertSame(
            "Error: myMessage",
            stream_get_contents($this->stream)
        );
    }

}

/* Original writer*/
class errorStreamWriter {
    public function log($message) {
        fwrite(STDERR, "Error: $message");
    }
}

// New writer:
class errorStreamWriter {

    protected $errorStream = STDERR;

    public function log($message) {
        fwrite($this->errorStream, "Error: $message");
    }

}

Он вынимает поток stderr и заменяет его потоком в памяти и считывает его обратно в тестовый файл, чтобы увидеть, был ли написан правильный вывод.

Обычно я бы точно сказал "Ввести путь к файлу в классе", но с STDERR, который не имеет для меня никакого смысла, поэтому это было бы моим решением.

phpunit stderrTest.php 
PHPUnit @[email protected] by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 5.00Mb

OK (1 test, 1 assertion)

Update

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

Просто наличие StreamWriter и построение его с помощью new StreamWriter(STDERR); приведет к хорошо тестируемому классу, который может быть повторно использован для множества целей в приложении без жесткого кодирования, что-то вроде "вот где ошибки идут" в классифицировать себя и добавлять гибкость.

Просто хотел добавить это как опцию, чтобы избежать "уродливых" вариантов теста:)

Ответ 3

Используйте фильтр потока для захвата/перенаправления вывода в STDERR. Следующий пример фактически повышает и перенаправляет на STDOUT, но передает основную идею.

class RedirectFilter extends php_user_filter {
  static $redirect;

  function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
  $bucket->data = strtoupper($bucket->data);
  $consumed += $bucket->datalen;
  stream_bucket_append($out, $bucket);
  fwrite(STDOUT, $bucket->data);
}
return PSFS_PASS_ON;
  }
}

stream_filter_register("redirect", "RedirectFilter")
or die("Failed to register filter");

function start_redirect() {
  RedirectFilter::$redirect = stream_filter_prepend(STDERR, "redirect", STREAM_FILTER_WRITE);
}

function stop_redirect() {
  stream_filter_remove( RedirectFilter::$redirect);
}

start_redirect();
fwrite(STDERR, "test 1\n");
stop_redirect();