У меня есть Logger
интерфейс, который принимает SplFileObject
в конструкторе для использования в качестве файла для этого конкретного журнала.Существует также метод log($timestamp, $message)
, который позволяет вести запись в журнал.В моей первой реализации при создании нового объекта и передаче только для чтения SplFileObject
должно быть выдано исключение.Я написал соответствующий модульный тест:
<?php
class FileLoggerTest extends PHPUnit_Framework_TestCase {
/**
* @expectedException \InvalidArgumentException
*/
public function testReadOnlyFileObjectFailure() {
$file = '/Library/WebServer/Documents/sprayfire/tests/mockframework/logs/test-log.txt';
$LogFile = new \SplFileObject($file);
$Logger = new \libs\sprayfire\logger\FileLogger($LogFile);
$Logger->log('test', 'something');
}
}
?>
Обычно у меня был бы метод создания имени каталога, но когда я начинал сталкиваться с проблемами, я менял его на абсолютный путь, чтобы исключить его в качестве причины..
А вот и реализация:
namespace libs\sprayfire\logger;
use \SplFileObject as SplFileObject;
use \InvalidArgumentException as InvalidArgumentException;
use libs\sprayfire\logger\Logger as Logger;
/**
* @brief A framework implemented class that adds a timestamp log message to
* the end of an injected file.
*/
class FileLogger implements Logger {
/**
* @brief A SplFileObject that should be used to write log messages to.
*
* @property $LogFile
*/
protected $LogFile;
/**
* @param $LogFile SplFileObject that should have log messages written to
*/
public function __construct(SplFileObject $LogFile) {
$this->LogFile = $LogFile;
$this->throwExceptionIfFileNotWritable();
}
/**
* @throws InvalidArgumentException
*/
protected function throwExceptionIfFileNotWritable() {
$isWritable = $this->LogFile->isWritable();
if (!$isWritable) {
throw new InvalidArgumentException('The passed file, ' . $this->LogFile->getPathname() . ', is not writable.');
}
}
/**
* @param $timestamp A formatted timestamp string
* @param $message The message string to log
* @return boolean true if the message was logged, false if it wasn't
*/
public function log($timestamp, $message) {
if (!isset($timestamp) || empty($timestamp)) {
$timestamp = 'No timestamp given';
}
if (!isset($message) || empty($message)) {
$message = 'Attempting to log an empty message';
}
$separator = ' := ';
$message = $timestamp . $separator . $message;
$wasWritten = $this->LogFile->fwrite($message);
if (!isset($wasWritten)) {
return false;
}
return true;
}
}
// End FileLogger
Проблема в том, что тест пройден, и я могу определить с помощью покрытия кода, сгенерированного тестом, что isWritable()
возвращает true иSplFileObject::fwrite()
для объекта, доступного только для чтения, также возвращает ненулевое значение.
Действительно, действительно странная часть этого состоит в том, что тот же самый код, запущенный в примере не модульного теста, терпит неудачу, как и должен.
$logFile = '/Library/WebServer/Documents/sprayfire/tests/mockframework/logs/test-log.txt';
$SplFile = new \SplFileObject($logFile);
$Logger = new \libs\sprayfire\logger\FileLogger($SplFile);
Запуск этого из index.php
приводит к тому, что xdebug показывает неперехваченное InvalidArgumentException
из FileLogger
с ожидаемым сообщением о том, что переданный файл недоступен для записи.Это полностью сбивает с толку, один и тот же точный код запускается в обеих ситуациях, но код внутри модульного теста «не работает», и код, не проверенный модулем, работает как положено.
- Да, файл существует.
SplFileObject
выдаст исключение, если этого не произойдет. - В обеих ситуациях выполняется один и тот же код, другой запускаемый код включает в себя настройку 2 констант, каталог файлов и ярлык для
DIRECTORY_SEPARATOR
и настройка автозагрузки классов.Но, опять же, это происходит одинаково в обеих ситуациях и может привести к сбою задолго до того, как этот модульный тест действительно будет запущен. - Help!
Просмотрсейчас проблема кажется относительно простой.PHP работает под пользователем _www
, а phpunit работает как пользователь, установивший его.Эти пользователи имеют разные разрешения, что имеет смысл.Если вы как-то столкнулись с этой проблемой, я предлагаю вам взглянуть на ответ edorian и переоценить, как вы пишете свои модульные тесты.