У меня есть класс, который выводит на STDERR
, но мне трудно найти способ заставить PHPUnit протестировать его вывод.
Класс PHPUnit_Extensions_OutputTestCase
также не работал.
У меня есть класс, который выводит на 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");
}
}
Он вынимает поток stderr и заменяет его потоком в памяти и считывает его обратно в тестовый файл, чтобы увидеть, был ли написан правильный вывод.
Обычно я бы точно сказал "Ввести путь к файлу в классе", но с STDERR
, который не имеет для меня никакого смысла, поэтому это было бы моим решением.
phpunit stderrTest.php
PHPUnit @[email protected] by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 5.00Mb
OK (1 test, 1 assertion)
После того, как я подумал, я бы сказал, что не может иметь никакого значения как 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();