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

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

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

Я не вижу способ буферизации 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 во внутренний буфер остается в качестве упражнения для читателя.

Вы не можете перехватывать и 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"); } } - <?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 @package_version@ by Sebastian Bergmann. . Time: 0 seconds, Memory: 5.00Mb OK (1 test, 1 assertion) 

Обновить

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

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

Просто хотел добавить это как вариант, чтобы избежать «уродливых» вариантов тестирования 🙂

Используйте фильтр потока для захвата / перенаправления вывода в 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();