2012-07-28 81 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()