2012-07-28 15 views
8

Estoy tratando de burlarme de una operación del sistema de archivos (bueno, de hecho, una lectura de la entrada php: //) con vfsStream, pero la falta de documentación decente y ejemplos realmente me está obstaculizando.Intentando probar las operaciones del sistema de archivos con VFSStream

El código relevante de la clase que soy prueba es el siguiente:

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; 
    } 
} 

La configuración que estoy usando en mi prueba PHPUnit es el siguiente:

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'); 
        })); 
} 

En una prueba real (que llama a un método que provoca indirectamente GetHandle()) trato de establecer el VFS y ejecutar una afirmación de la siguiente manera:

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

Esto solo hace que la prueba se cuelgue.

Obviamente estoy haciendo algo muy malo aquí, pero dado el estado de la documentación, no puedo determinar qué es lo que estoy destinado a hacer. ¿Esto es algo causado por vfsstream, o phpunit se burla de lo que necesito ver aquí?

+0

vfsStream es compleja pieza innecesaria de kit con todas las subclases y espacios de nombres, etc. El todo debe ser una enfoque de clase Es un envoltorio para una envoltura. He escrito solo una clase basada en la interfaz de php StreamWrapper y eso es suficiente para hacer el trabajo. Ver también: http://php.net/manual/en/class.streamwrapper.php – Codebeat

Respuesta

9

Entonces ... ¿cómo probar con transmisiones? Todo lo que vfsStream hace es proporcionar un contenedor de flujo personalizado para las operaciones del sistema de archivos. No necesita la biblioteca vfsStream completa para simular el comportamiento de un único argumento de transmisión, no es la solución correcta. En su lugar, debe escribir y registrar su propia envoltura de flujo único porque no está tratando de burlarse de las operaciones del sistema de archivos.

Digamos que tiene la siguiente clase simple para probar:

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

En la vida real que hace:

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

Así que para probarlo, creamos nuestra propia envoltura de secuencia que nos va a permitir controlar el comportamiento de la secuencia que pasamos. No puedo entrar en demasiados detalles porque las envolturas de transmisión personalizadas son un tema de gran tamaño. Pero básicamente el proceso es así:

  1. Crear envoltura de secuencia personalizada
  2. registro que envoltura de secuencia con PHP
  3. abierto un flujo de recursos utilizando el esquema de envoltura de secuencia registrada

Por lo que su flujo personalizado se ve algo así como:

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; 
    } 
} 

Luego, en el caso de test que podría hacer esto:

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'); 
} 

A continuación, sólo tiene que cambiar las propiedades estáticas he definido en la envoltura de secuencia para cambiar los datos que regresa de la lectura de la corriente. O extienda su clase de envoltura de secuencia base y regístrela en su lugar para proporcionar diferentes escenarios para las pruebas.

Esta es una introducción muy básica, pero el punto es este: no use vfsStream a menos que se esté burlando de las operaciones reales del sistema de archivos, para eso está diseñado. De lo contrario, escriba un contenedor de flujo personalizado para la prueba.

PHP proporciona una clase envoltura de secuencia prototipo para empezar: http://www.php.net/manual/en/class.streamwrapper.php

+1

Supongo que tendré que dejar de encontrar VFS para otro día. – GordonM

0

he tenido problemas con la búsqueda de una respuesta similar - He encontrado la documentación que carece también.

que sospecha que su problema era que vfs://testReqBody/data no era una ruta a un archivo existente, (como php://input siempre habrá.)

Si la respuesta aceptada es una respuesta aceptable, entonces este es el equivalente con vfsStreamWrapper.

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

Alternativamente, si usted necesita para dividir esto, de manera que se define el contenido después de llamar vfsStream::setup(), esta es la forma.

<?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')); 

otra cosa a tener en cuenta a partir del código original, no es necesario llamar al usar vfsStreamWrapper::register();vfsStream::setup()

Cuestiones relacionadas