1

その特定のログのファイルとして使用するコンストラクターで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()、読み取り専用オブジェクトでもnull以外の値を返すことです。

これの本当に、本当に奇妙な部分は、単体テスト以外の例で実行されたのとまったく同じコードが、本来あるべきように失敗することです。

$logFile = '/Library/WebServer/Documents/sprayfire/tests/mockframework/logs/test-log.txt';
$SplFile = new \SplFileObject($logFile);
$Logger = new \libs\sprayfire\logger\FileLogger($SplFile);

これをfromから実行するとindex.php、xdebugは、渡されたファイルが書き込み可能ではないという予期されたメッセージとともに、キャッチInvalidArgumentExceptionされていないことを示します。FileLoggerこれは完全に不可解であり、両方の状況で同じ正確なコードが実行されていますが、単体テスト内のコードは「失敗」しており、単体テストされていないコードは期待どおりに実行されています。


  1. はい、ファイルは存在します。 SplFileObjectそうでない場合は例外をスローします。
  2. まったく同じコードが両方の状況で実行されています。実行されている他のコードには、2つの定数、ファイルディレクトリとへのショートカットのDIRECTORY_SEPARATOR設定、およびクラスの自動読み込みの設定が含まれます。ただし、これも両方の状況でまったく同じように発生し、この単体テストが実際に実行されるずっと前に失敗する可能性があります。
  3. ヘルプ!

今それを見ると、問題は比較的単純に見えます。PHPは_wwwユーザーの下で実行されており、phpunitはそれをインストールしたユーザーとして実行されています。これらのユーザーにはさまざまな権限があり、これは完全に理にかなっています。どういうわけかこの問題が発生している場合は、edorianの回答を確認し、単体テストの作成方法を再評価することをお勧めします。

4

1 に答える 1

5

最初に:

ユニットテストにはがありSplTempFileObject extends SplFileObjectます。

とにかく遅いので、通常はディスク上に実際のファイルを作成する必要はありません;)

phpunitテストのisReadable/isWriteableチェックでは、通常、ディスク上に読み取り/書き込み不可能なファイルを作成するエーテルを作成するか、該当する場合はvfsStreamWrapperを使用します。また、で動作しSplFileObjectます。

私たちの問題:

テストでは、最後の行を削除する必要があります。構築中の例外を除いて、それを取り除こう;)

私が奇妙だと思ったのは、そこに絶対的な道があるということです。ルートフォルダの構造は本当にで始まります'/Library/WebServer/Documents/か?これは、テストが「WebServer」ディレクトリにあることを意味するため、主に混乱しています。とにかく..次に進む:

「私のために働く」

添付されているのは、期待どおりに機能して例外をスローするスタンドアロンバージョンのテストです。

問題はセットアップのどこかにあるようだと言うことは別として、ここでできることはあまりありません。たぶん、InvalidArgumentExceptionなしで試して/みるか、単独で/新しく作成されたファイルでテストを試してください。

PHPUnitはファイル処理機能に干渉しないので、それ以外は考えられません。以下のサンプルコードは役に立ちますか?:)

phpunit mep.php 
PHPUnit 3.6.5 by Sebastian Bergmann.

.

Time: 1 second, Memory: 5.00Mb

OK (1 test, 1 assertion)

<?php

class FileLoggerTest extends PHPUnit_Framework_TestCase {

    /**
     * @expectedException InvalidArgumentException
     */
    public function testReadOnlyFileObjectFailure() {
        $file = __DIR__."/_files/test-log.txt";
        touch($file);
        chmod($file, 0444);
        $LogFile = new \SplFileObject($file);
        $Logger = new FileLogger($LogFile);
    }

}


class FileLogger {

    protected $LogFile;
    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;
    }
}
于 2011-12-26T20:34:48.733 に答える