2009-07-10 7 views
6

Ich benutze das versteckte Hash-Element von CSRF mit Zend_Form und versuche Unit Unit die Anmeldung zu testen, aber ich weiß nicht, wie man einen Unit Test schreibt, um dieses Element einzuschließen. Sah in der Dokumentation und las so viele Tutorials wie ich finden konnte. Ich sogar delicioused them all, aber niemand erwähnt dies.Wie testet ihr eine Zend_Form, die das CSRF-Formularelement enthält?

+1

Update: Es ist ein bisschen kludgy, aber ich war in der Lage eine prüfbare Form zu bekommen, weil ich Laden bin meine Zend_Form aus einer .ini-Datei. Ich habe die csrf-Sektion in eine reine Testing-Sektion aufgeteilt, so dass meine Tests eine Anmeldung ohne Durchlaufen der csrf erlauben. Hoffe das hilft jemand anderem. BTW, ich denke, eine .ini-Datei ist der beste Ort, um Ihre Formulare zu laden. – joedevon

+0

Der Entwickler von PHPUnit hat mir gesagt, dass "End-to-End Testing! = Unit Testing", was natürlich stimmt. In diesem Fall war es mir egal, den csrf so weit zu bekommen, wie ich testen wollte, dass ich mich anmelden und ein Element auf der resultierenden Seite sehen konnte. Nicht in der Lage, dieses Element in den Test aufzunehmen, war das Löschen der Anmeldung ... – joedevon

Antwort

3

Der richtige Hash ist in der Sitzung gespeichert, und das Hash-Formularelement hat eine Zend_Session_Namespace-Instanz, die den Namespace für den Hash enthält.

um Unit-Test das Element, würden Sie die Zend_Session_Namespace Instanz im Element ersetzen (mit setSession) mit einem selbst erstellten, welche die richtige Hash enthält

Weitere (der Hash wird in Taste „Raute“ gespeichert) Beispiele, die Sie wahrscheinlich an den Zend Framework-Unit-Tests für die Zend_Form_Element_Hash-Klasse sehen könnten. Ich nehme an, dass sie sich auch damit befassen mussten.

+0

Ich habe das Gefühl, dass etwas falsch ist, wenn man so viel über die Implementierung von CSRF, um Tests für Controller zu schreiben, die es verwenden. –

+0

In einem geschlossenen Fall würden Sie wahrscheinlich das gesamte CSRF-Formularelement durch eine mocked-Instanz ersetzen, dann müssen Sie sich nicht mit dem Session-Speicher beschäftigen –

1

ich eine Umgebungsvariable in meiner Apache vhost-Datei festgelegt, die den Code, den Server sagt es läuft auf: Entwicklung, Inszenierung oder Produktion

Die Linie für die vhost-Datei ist:

SetEnv SITE_ENV "dev" 

Dann mache ich nur meine Formulare an die entsprechende Umgebung reagieren:

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

ich diese Technik für viele Sachen verwenden. Zum Beispiel, wenn es Dev ist, leite ich alle ausgehende E-Mail an mich, etc.

+0

Das ist ähnlich, was ich in der forms.ini-Datei getan habe ... Ich weiß nicht warum, aber kein Fan von IF-Anweisungen außerhalb von Gerüst- oder INI-Dateien für die umgebungsspezifische Handhabung im Hauptcode. Es scheint nur so, als würde eine neue Umgebung das Suchen in einer Menge von Orten erfordern, um es zu reparieren anstatt .ini und bootstrap, wo man Unterschiede erwartet .... – joedevon

9

Der Csrf-Wert wird jedes Mal generiert, wenn das Formular gerendert wird. Verstecktes Element des Formulars wird mit diesem Wert vorbefüllt. Dieser Wert wird auch in der Sitzung gespeichert. Nach dem Absenden des Formulars überprüft die Validierung, ob der Wert aus dem Formular in der Sitzung gespeichert wird, andernfalls schlägt die Validierung fehl. Es ist wichtig, dass das Formular während des Tests gerendert werden muss (damit es den versteckten Wert erzeugen und in der Sitzung speichern kann), dann können wir den versteckten Wert aus dem gerenderten HTML extrahieren, und später können wir versteckten Hash-Wert hinzufügen unsere Anfrage. Betrachten Sie dieses Beispiel:

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

Dies sieht vielversprechend aus, aber $ html = $ this-> getResponse() -> getBody(); 'ist in meinem Fall immer leer. – takeshin

+0

@takeshin Vergessen Sie nicht, die Seite mit dem Formular zu versenden, bevor Sie nach einem Körper fragen;) –

+0

#als bis heute, führte ich Unit Tests mit diesem Ansatz und es funktioniert. +1 zu Lukas. –

1

ich eine neuere Frage beantwortet ähnlich wie diese. Ich stelle meine Antwort hier auch für den Fall, dass es irgendjemanden in der Zukunft hilft.

Ich habe kürzlich eine gute Möglichkeit gefunden, Formulare mit Hash-Elementen zu testen. Dies wird ein Mock-Objekt verwenden, um das Hash-Element auszublenden, und Sie müssen sich nicht darum kümmern. Sie müssen nicht einmal einen session_start oder irgendetwas auf diese Weise machen. Sie müssen das Formular auch nicht "vorrendern".

zuerst eine 'Stub' Klasse erstellen, wie so

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

Dann auf die Form irgendwo folgendes hinzuzufügen.

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

Die Set-Methode gibt es nur für Testzwecke wirklich. Sie werden es wahrscheinlich nicht wirklich benutzen, aber in phpunit können Sie Folgendes richtig machen.

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

Sie müssen Ihren eigenen Stub erstellen.Sie können nicht einfach die phpunit-Methode getMockObject() aufrufen, weil dadurch das Hash-Element direkt erweitert wird und das normale Hash-Element "bösen" Kram im Konstruktor ausführt.

Mit dieser Methode müssen Sie nicht einmal mit einer Datenbank verbunden sein, um Ihre Formulare zu testen! Ich brauchte eine Weile, um darüber nachzudenken.

Wenn Sie möchten, können Sie die Methode setHashElement() (zusammen mit der Variablen und der Methode get) in eine FormAbstract-Basisklasse schieben.

ERINNERN, in phpunit müssen Sie das Hash-Element während der Formularkonstruktion übergeben. Wenn Sie dies nicht tun, wird Ihre init()-Methode aufgerufen, bevor Ihr Stub-Hash mit der set-Methode gesetzt werden kann und Sie am Ende das reguläre Hash-Element verwenden. Sie wissen, dass Sie das reguläre Hash-Element verwenden, da Sie wahrscheinlich einen Sitzungsfehler erhalten, wenn Sie NICHT mit einer Datenbank verbunden sind.

Lassen Sie mich wissen, wenn Sie das hilfreich finden oder wenn Sie es verwenden.

1

Lösung für ZF2 Ihre Form in Test schafft, und von Ihrem csrf Formularelement bekommen Wert:

 $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

Danke, dass du mir viel Zeit gespart hast – Hooli