2012-07-28 82 views
8

我試圖用vfsStream來模擬文件系統操作(實際上是從php://輸入讀取),但缺乏體面的文檔和示例實際上阻礙了我。試圖用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'); 
        })); 
} 

在實際測試(它調用間接觸發getHandle()的方法)我嘗試設置VFS並按如下方式運行斷言:

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的類,這足以完成工作。另請參閱:http://php.net/manual/en/class.streamwrapper.php – Codebeat 2013-08-02 23:01:45

回答

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. 創建自定義流包裝
  2. 註冊與PHP
  3. 打開使用註冊的流包裝方案

所以,資源流,其流包裝你的自定義流如下所示:

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 2012-07-28 21:57:51

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()