2012-07-28 2 views
8

Sto provando a prendere in giro un'operazione di filesystem (beh, in realtà una lettura da php: // input) con vfsStream ma la mancanza di documentazione ed esempi decenti mi sta davvero ostacolando.Provare a testare le operazioni del filesystem con VFSStream

Il relativo codice dalla classe Sono test è la seguente:

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 messa a punto che sto utilizzando nel mio test PHPUnit è la seguente:

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

In una prova reale (che chiama un metodo che innesca indirettamente getHandle()) io cerco di impostare il VFS ed eseguire un'affermazione come segue:

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

Ciò causa solo il blocco del test.

Ovviamente sto facendo qualcosa di molto sbagliato qui, ma dato lo stato della documentazione non sono in grado di capire cosa si debba fare. Questo è qualcosa causato da vfsstream, o è phpunit che deride la cosa che devo guardare qui?

+0

VFSStream è inutile complesso pezzo di kit con tutte le sottoclassi e spazi dei nomi, ecc Il tutto deve essere un approccio di classe. È un involucro per un involucro. Ho scritto solo una classe basata sull'interfaccia php StreamWrapper e questo è sufficiente per completare il lavoro. Vedi anche: http://php.net/manual/en/class.streamwrapper.php – Codebeat

risposta

9

Quindi ... come eseguire il test con i flussi? Tutto ciò che vfsStream fa è fornire un wrapper stream personalizzato per le operazioni del file system. Non hai bisogno della libreria vfsStream in piena regola solo per prendere in giro il comportamento di un argomento di un singolo stream: non è la soluzione corretta. Invece, è necessario scrivere e registrare il proprio wrapper di flusso unico perché non si sta tentando di simulare le operazioni del file system.

Diciamo che avete la seguente classe semplice prova:

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

Nella vita reale si fa:

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

Quindi, per provarlo, creiamo la nostra flusso wrapper che ci permetterà di controlla il comportamento del flusso che passiamo. Non posso entrare troppo nel dettaglio perché i wrapper di flusso personalizzati sono un argomento importante. Ma fondamentalmente il processo va in questo modo:

  1. Crea flusso personalizzato involucro
  2. Registrati che involucro torrente con PHP
  3. aprire un flusso di risorse utilizzando lo schema di involucro flusso registrato

Quindi la tua flusso personalizzato è simile a:

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

Poi, nel tuo caso di test si farebbe questo:

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

Poi, si può semplicemente cambiare le proprietà statiche ho definito nella confezione flusso di cambiare ciò che i dati torna dalla lettura del flusso. In alternativa, estendi la classe wrapper stream di base e registrala invece per fornire scenari diversi per i test.

Questa è un'introduzione molto semplice, ma il punto è questo: non utilizzare vfsStream a meno che non stiate prendendo in giro le reali operazioni del filesystem - questo è quello per cui è stato progettato. Altrimenti, scrivi un wrapper stream personalizzato per il test.

PHP fornisce un prototipo classe di flusso involucro per iniziare: http://www.php.net/manual/en/class.streamwrapper.php

+1

Indovina che dovrò lasciare capire VFS per un altro giorno. – GordonM

0

ho lottato con la ricerca di una risposta simile - Ho trovato la documentazione mancante anche.

ho il sospetto il problema era che vfs://testReqBody/data non era un percorso di un file esistente, (come php://input saranno sempre.)

Se la risposta accettata è una risposta accettabile, allora questo è l'equivalente di vfsStreamWrapper.

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

In alternativa, se avete bisogno di dividere questo in su, in modo tale che si definiscono i contenuti dopo aver chiamato vfsStream::setup(), questo è il modo.

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

Un'altra cosa da notare dal codice originale, non è necessario chiamare vfsStreamWrapper::register(); quando si utilizza vfsStream::setup()