2009-07-10 14 views
6

Sto usando l'elemento hash nascosto CSRF con Zend_Form e sto provando a Unit Test per l'accesso ma non so come scrivere un Test unitario per includere quell'elemento. Ho consultato i documenti e ho letto tutti i tutorial che riuscivo a trovare. Ho anche delicioused them all, ma nessuno lo menziona.Come si esegue il test dell'unità su un Zend_Form che include l'elemento del modulo CSRF?

+1

Aggiornamento: E 'un po' kludgy, ma sono stato in grado di ottenere una forma verificabile perché io sono carico il mio Zend_Form da un file .ini. Ho suddiviso la sezione csrf in una sezione di solo test, quindi i miei test consentono un accesso senza passare attraverso il csrf. Spero che questo aiuti qualcun altro. A proposito, penso che un file .ini sia il posto migliore in cui caricare i tuoi moduli. – joedevon

+0

Il creatore di PHPUnit mi ha detto che "Test end-to-end! = Unit Testing", che è ovviamente vero. In questo caso non mi importava di ottenere il csrf in quanto volevo testare che potevo accedere e vedere alcuni elementi nella pagina risultante. Non essere in grado di includere questo elemento nel test stava uccidendo il login ... – joedevon

risposta

3

la correttezza hash è memorizzato nella sessione, e l'elemento modulo Hash ha un'istanza Zend_Session_Namespace che contiene lo spazio dei nomi per l'hash.

Per unit test l'elemento, si dovrebbe sostituire l'istanza Zend_Session_Namespace nell'elemento (con setSession) con una sola creato da voi, che contiene l'hash corretto (l'hash viene memorizzato nel tasto "cancelletto")

Per ulteriori esempi che potresti probabilmente esaminare i test delle unità di Zend Framework per la classe Zend_Form_Element_Hash. Immagino che abbiano avuto a che fare anche con questo.

+0

Mi sembra che ci sia qualcosa di sbagliato quando devi sapere molto sull'implementazione di CSRF per scrivere test per controller che lo utilizzano. –

+0

In un caso più contenuto probabilmente si sostituisce l'intero elemento del modulo CSRF con un'istanza fittata, quindi non sarà necessario occuparsi della memoria di sessione –

1

ho impostare una variabile di ambiente nel mio file vhost Apache, che racconta il codice quale server è in esecuzione su: sviluppo, messa in scena, o di produzione

La linea per il file vhost è:

SetEnv SITE_ENV "dev" 

Poi ho solo fare le mie forme reagiscono all'ambiente appropriato:

if($_SERVER['SITE_ENV']!='dev') 
{ 
    $form_element->addValidator($csrf_validator); 
} 

io uso la stessa tecnica per un sacco di roba. Per esempio, se si tratta di dev, che reindirizzare tutte le email in uscita per me, ecc

+0

Questo è simile a quello che ho finito nel file forms.ini ... Non so perché ma non un fan delle dichiarazioni IF al di fuori del file di scaffolding o .ini per la gestione specifica dell'ambiente all'interno del codice principale. Sembra che un nuovo ambiente richieda di cercare in un sacco di posti per risolvere il problema piuttosto che .ini e bootstrap in cui ti aspetti delle differenze .... – joedevon

9

Il valore Csrf viene generato ogni volta che viene eseguito il rendering del modulo. L'elemento nascosto del modulo viene precompilato con quel valore. Questo valore viene anche memorizzato in sessione. Dopo aver inviato il modulo, la convalida verifica se il valore inviato dal modulo è memorizzato in sessione, in caso contrario la convalida non riesce. È essenziale che il modulo debba essere sottoposto a rendering durante il test (in modo che possa generare il valore nascosto e archiviarlo in sessione), quindi è possibile estrarre il valore nascosto da html renderizzato e successivamente aggiungere il valore hash nascosto in la nostra richiesta. Considerate questo esempio:

function testAddPageStoreValidData() 
{ 
    // render the page with form 
    $this->dispatch('/form-page'); 

    // fetch content of the page 
    $html = $this->getResponse()->getBody(); 

    // parse page content, find the hash value prefilled to the hidden element 
    $dom = new Zend_Dom_Query($html); 
    $csrf = $dom->query('#csrf')->current()->getAttribute('value'); 

    // reset tester for one more request 
    $this->resetRequest() 
     ->resetResponse(); 

    // now include $csrf value parsed from form, to the next request 
    $this->request->setMethod('POST') 
        ->setPost(array('title'=>'MyNewTitle', 
            'body'=>'Body', 
            'csrf'=>$csrf)); 
    $this->dispatch('/form-page'); 

    // ... 
} 
+0

Questo sembra promettente, tuttavia '$ html = $ this-> getResponse() -> getBody(); 'è sempre vuoto nel mio caso. – takeshin

+0

@takeshin non dimenticare di spedire la pagina con il modulo prima di chiedere un corpo;) –

+0

# come da data, ho eseguito test unitari usando questo approccio e funziona. +1 a Lukas. –

1

ho risposto a una domanda più recente, simile a questo. Sto mettendo la mia risposta anche qui nel caso in cui aiuti qualcuno in futuro.

Recentemente ho trovato un ottimo modo di testare moduli con elementi hash. Questo userà un oggetto fittizio per eliminare l'elemento hash e non dovrai preoccuparti di questo. Non dovrai nemmeno fare un session_start o qualcosa in questo modo. Non dovrai neanche 'prerender' il modulo.

prima creare un 'stub' di classe in questo modo

class My_Form_Element_HashStub extends Zend_Form_Element_Hash 
{ 
    public function __construct(){} 
} 

Quindi, aggiungere il seguente alla forma da qualche parte.

class MyForm extends Zend_Form 
{ 

    protected $_hashElement; 

    public function setHashElement(Zend_Form_Hash_Element $hash) 
    { 
     $this->_hashElement = $hash; 
     return $this; 
    } 

    protected function _getHashElement($name = 'hashElement') 
    { 
     if(!isset($this->_hashElement) 
     { 
      if(isset($name)) 
      { 
       $element = new Zend_Form_Element_Hash($name, 
                array('id' => $name)); 
      } 
      else 
      { 
       $element = new Zend_Form_Element_Hash('hashElement', 
             array('id' => 'hashElement')); 
      } 

      $this->setHashElement($element); 
      return $this->_hashElement; 
     } 
    } 

    /** 
    * In your init method you can now add the hash element like below 
    */ 
    public function init() 
    { 
     //other code 
     $this->addElement($this->_getHashElement('myotherhashelementname'); 
     //other code 
    } 
} 

Il metodo impostato è proprio a scopo di test. Probabilmente non lo userai affatto durante l'uso reale ma ora in phpunit puoi fare ciò che segue.

class My_Form_LoginTest extends PHPUnit_Framework_TestCase 
{ 

    /** 
    * 
    * @var My_Form_Login 
    */ 
    protected $_form; 
    /** 
    * 
    * @var PHPUnit_Framework_MockObject_MockObject 
    */ 
    protected $_hash; 

    public function setUp() 
    { 
     parent::setUp(); 
     $this->_hash = $this->getMock('My_Form_Element_HashStub'); 

     $this->_form = new My_Form_Login(array(
        'action'     => '/', 
        'hashElement'    => $this->_hash 
    } 

    public function testTrue() 
    { 
     //The hash element will now always validate to true 
     $this->_hash 
      ->expects($this->any()) 
      ->method('isValid') 
      ->will($this->returnValue(true)); 

     //OR if you need it to validate to false 
     $this->_hash 
      ->expects($this->any()) 
      ->method('isValid') 
      ->will($this->returnValue(true)); 
    } 
} 

È necessario creare il proprio stub.Non puoi semplicemente chiamare il metodo phpunit getMockObject() perché questo estenderà direttamente l'elemento hash e il normale elemento hash fa 'male' nel suo costruttore.

Con questo metodo non è nemmeno necessario essere connessi a un database per testare i moduli! Mi ci è voluto un po 'per pensarci.

Se lo si desidera, è possibile premere il metodo setHashElement() (insieme alla variabile e al metodo get) in una classe di base FormAbstract.

RICORDARE, in phpunit si deve passare l'elemento hash durante la costruzione della forma. Se non lo fai, il tuo metodo init() verrà chiamato prima che il tuo hash stub possa essere impostato con il metodo set e finirai per utilizzare l'elemento hash regolare. Saprai che stai utilizzando l'elemento di hash regolare perché probabilmente otterrai un errore di sessione se NON sei connesso a un database.

Fatemi sapere se trovate utile o se lo usate.

1

Soluzione per ZF2 sta creando il modulo in prova, e ottenere valore dalla vostra forma CSRF elemento:

 $form = new \User\Form\SignupForm('create-user'); 
     $data = [ 
      'security' => $form->get('security')->getValue(), 
      'email' => '[email protected]', 
      'password' => '123456', 
      'repeat-password' => '123456', 
     ]; 
     $this->dispatch('/signup', 'POST', $data); 
+0

grazie mi hai risparmiato un sacco di tempo – Hooli

Problemi correlati