2012-07-28 8 views
8

私はファイルシステム操作(よく実際にはphp://入力からの読み込み)をvfsStreamでモックしようとしていますが、まともな文書や例がないと本当に邪魔です。次のようにVFSStreamでファイルシステム操作をテストしようとしています

私がテストしていたクラスからの関連するコードは次のとおりです。

class RequestBody implements iface\request\RequestBody 
{ 
    const 
     REQ_PATH = 'php://input', 

    protected 
     $requestHandle = false; 

    /** 
    * Obtain a handle to the request body 
    * 
    * @return resource a file pointer resource on success, or <b>FALSE</b> on error. 
    */ 
    protected function getHandle() 
    { 
     if (empty ($this -> requestHandle)) 
     { 
      $this -> requestHandle = fopen (static::REQ_PATH, 'rb'); 
     } 
     return $this -> requestHandle; 
    } 
} 

次のように私は私のPHPUnitのテストで使用している設定は次のとおりです。

protected function configureMock() 
{ 
    $mock = $this -> getMockBuilder ('\gordian\reefknot\http\request\RequestBody'); 

    $mock -> setConstructorArgs (array ($this -> getMock ('\gordian\reefknot\http\iface\Request'))) 
      -> setMethods (array ('getHandle')); 


    return $mock; 
} 

/** 
* Sets up the fixture, for example, opens a network connection. 
* This method is called before a test is executed. 
*/ 
protected function setUp() 
{ 
    \vfsStreamWrapper::register(); 
    \vfsStream::setup ('testReqBody'); 

    $mock = $this -> configureMock(); 
    $this -> object = $mock -> getMock(); 

    $this -> object -> expects ($this -> any()) 
        -> method ('getHandle') 
        -> will ($this -> returnCallback (function() { 
         return fopen ('vfs://testReqBody/data', 'rb'); 
        })); 
} 

を実際の試験では私はVFSを設定し、次のように主張を実行しよう(これは間接的にgetHandle()をトリガーするメソッドを呼び出します):

public function testBodyParsedParsedTrue() 
{ 
    // Set up virtual data 
    $fh  = fopen ('vfs://testReqBody/data', 'w'); 
    fwrite ($fh, 'test write 42'); 
    fclose ($fh); 
    // Make assertion 
    $this -> object -> methodThatTriggersGetHandle(); 
    $this -> assertTrue ($this -> object -> methodToBeTested()); 
} 

これにより、テストがハングするだけです。

明らかに私はここで何か間違っていますが、ドキュメンテーションの状態を考えると、私はそれが何であるかを理解することができません。これは何かvfsstreamによって引き起こされたものなのでしょうか、あるいは私がここで調べる必要があることをphpunitが嘲笑していますか?

+0

VFSStreamすべてがでなければならないすべてのサブクラスと名前空間などとキットの不必要な複雑な部分であるを使用しているとき、あなたはvfsStreamWrapper::register();を呼び出す必要はありませんクラスアプローチ。これはラッパーのラッパーです。私はPHPインターフェイスStreamWrapperに基づいて1つだけのクラスを書いており、それは仕事を完了するのに十分です。以下も参照してください:http://php.net/manual/en/class.streamwrapper.php – Codebeat

答えて

9

ストリームでテストするには?すべてのvfsStreamは、ファイルシステム操作用のカスタムストリームラッパーを提供します。単一のストリーム引数の振る舞いを模倣するために、本格的なvfsStreamライブラリは必要ありません。これは正しい解決策ではありません。代わりに、ファイルシステム操作を模擬しようとしていないため、独自の単発ストリームラッパーを作成して登録する必要があります。

はあなたがテストするには、次の単純なクラスを持っていると言う:実際の生活の中で

class ClassThatNeedsStream { 
    private $bodyStream; 
    public function __construct($bodyStream) { 
     $this->bodyStream = $bodyStream; 
    } 
    public function doSomethingWithStream() { 
     return stream_get_contents($this->bodyStream); 
    } 
} 

あなたは:

$phpInput = fopen('php://input', 'r'); 
new ClassThatNeedsStream($phpInput); 

だから、それをテストするために、私たちは私たちができるようになります私たち自身のストリームラッパーを作成します私たちが渡すストリームの振る舞いを制御します。カスタムストリームラッパーは大きな話題なので、あまり詳しく説明することはできません。 しかしは、基本的には、プロセスは次のようになります:

  1. が登録PHP
  2. オープン

ので、登録されたストリームラッパー・スキームを使用してリソースストリームとそのストリームラッパーをカスタムストリームラッパー

  • を作成し、あなたのカスタムストリームは次のようになります。

    class TestingStreamStub { 
    
        public $context; 
        public static $position = 0; 
        public static $body = ''; 
    
        public function stream_open($path, $mode, $options, &$opened_path) { 
         return true; 
        } 
    
        public function stream_read($bytes) { 
         $chunk = substr(static::$body, static::$position, $bytes); 
         static::$position += strlen($chunk); 
         return $chunk; 
        } 
    
        public function stream_write($data) { 
         return strlen($data); 
        } 
    
        public function stream_eof() { 
         return static::$position >= strlen(static::$body); 
        } 
    
        public function stream_tell() { 
         return static::$position; 
        } 
    
        public function stream_close() { 
         return null; 
        } 
    } 
    

    は、その後、あなたのテストケースでは、これを行うだろう:

    public function testSomething() { 
        stream_wrapper_register('streamTest', 'TestingStreamStub'); 
        TestingStreamStub::$body = 'my custom stream contents'; 
        $stubStream = fopen('streamTest://whatever', 'r+'); 
    
        $myClass = new ClassThatNeedsStream($stubStream); 
        $this->assertEquals(
         'my custom stream contents', 
         $myClass->doSomethingWithStream() 
        ); 
    
        stream_wrapper_unregister('streamTest'); 
    } 
    

    その後、あなたは単に私がストリームを読んでから戻ってきたどのデータに変更するストリームラッパーで定義した静的プロパティを変更することができます。または、ベースストリームのラッパークラスを拡張し、代わりに登録してテストのさまざまなシナリオを提供します。

    これは非常に基本的なイントロですが、実際のファイルシステム操作を嘲笑している場合を除き、vfsStreamを使用しないでください。それ以外の場合は、テスト用のカスタムストリームラッパーを作成します。

    PHPは、あなたが始めるためにプロトタイプストリームラッパークラスを提供します:http://www.php.net/manual/en/class.streamwrapper.php

  • +1

    もう一日VFSを考え直す必要があります。 – GordonM

    0

    私は同様の答えを見つけることに苦労している - 私はまた欠けてドキュメントを見つけました。

    は、私はあなたの問題がvfs://testReqBody/dataは、既存のファイルへのパスではないということだった疑い(php://inputが常になりますよう。)

    受け入れ答えが許容答えであれば、これはvfsStreamWrapperと同等です。また

    <?php 
    // ... 
    $reqBody = "Request body contents" 
    vfsStream::setup('testReqBody', null, ['data' => $reqBody]); 
    $this->assertSame($reqBody, file_get_contents('vfs://testReqBody/data')); 
    

    、あなたがこれを分割する必要がある場合、あなたはvfsStream::setup()を呼び出した後に内容を定義するように、これはどのようにあります。

    <?php 
    //... 
    $reqBody = "Request body contents" 
    $vfsContainer = vfsStream::setup('testReqBody'); 
    vfsStream::newFile('data')->at($vfsContainer)->setContent($reqBody); 
    $this->assertSame($reqBody, file_get_contents('vfs://testReqBody/data')); 
    

    あなたの元のコードから注意すべきもうひとつvfsStream::setup()