2009-07-10 73 views
6

我正在使用CSRF隱藏哈希元素與Zend_Form,並試圖單元測試登錄,但不知道如何編寫單元測試,以包括該元素。在文檔中查找並閱讀儘可能多的教程。我甚至delicioused them all,但沒有人提到這一點。你如何測試一個包含CSRF表單元素的Zend_Form?

+1

更新:這是一個有點缺憾,但我能得到一個可測試的形式,因爲我裝我的Zend_Form來自.ini文件。我將csrf部分分解爲僅測試部分,這樣我的測試就可以在不通過csrf的情況下進行登錄。希望這可以幫助別人。順便說一句,我認爲一個.ini文件是加載你的表格的最佳地方。 – joedevon 2009-07-11 00:07:36

+0

PHPUnit的創建者向我提到「端到端測試!=單元測試」,這當然是事實。在這種情況下,我並不在乎如何獲取csrf,因爲我想測試我可以登錄並在結果頁面上看到某個元素。不能在測試中包含此元素正在殺死登錄... – joedevon 2009-07-12 05:43:03

回答

3

正確的散列存儲在會話中,並且散列表單元素具有包含散列名稱空間的Zend_Session_Namespace實例。

進行單元測試的元素,你將與一個替代的Zend_Session_Namespace實例中的元素(setSession)您自己創建其中包含正確的散列(哈希存儲在關鍵的「哈希」)

爲了進一步你可以看看Zend Framework的單元測試Zend_Form_Element_Hash類的例子。我想他們也必須處理這個問題。

+0

我覺得有什麼問題,當你必須知道這麼多關於實施CSRF爲使用它的控制器編寫測試。 – 2011-10-24 01:31:51

+0

在更復雜的情況下,您可能會用模擬實例替換整個CSRF表單元素,那麼您將不需要處理會話存儲的東西 – 2011-10-24 08:01:02

1

我在我的Apache虛擬主機文件,它告訴哪個服務器運行它的代碼設置環境變量: 開發,分期或生產

的虛擬主機文件中的行是:

SetEnv SITE_ENV "dev" 

然後,我只是讓我的形式作出反應,適當的環境:

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

我用的東西很多相同的技術。例如,如果它是開發者,我將所有外發電子郵件重定向到我等。

+0

這與我在forms.ini文件中做的事情類似... 我不知道爲什麼但不是IF語句的粉絲在腳手架或.ini文件之外,用於主代碼內部的環境特定處理。看起來像一個新的環境將需要尋找在很多地方修復它,而不是.ini和bootstrap在你期望的差異.... – joedevon 2009-07-13 04:58:04

9

每次渲染表單時都會生成Csrf值。該表單的隱藏元素會預先填充該值。該值也被存儲在會話中。 提交表單後,驗證會檢查從表單發佈的值是否存儲在會話中,如果不存在,則驗證將失敗。必須在測試期間呈現表單(因此它可以生成隱藏值並將其存儲到會話中),然後我們可以從呈現的html中提取隱藏值,然後我們可以將隱藏的哈希值添加到我們的要求。 考慮這個例子:

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

    // ... 
} 
1

我回答類似這樣的一個較新的問題。我也會在這裏提供我的答案,以便將來幫助任何人。

我最近發現了一個用哈希元素測試表單的好方法。這將使用一個模擬對象來存儲哈希元素,你不必擔心它。你甚至不需要這樣做session_start或任何東西。您不必「預渲染」表單。

首先創建一個像這樣

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

一個「存根」級之後,添加以下形式的地方。

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

設置方法只是爲了測試的目的。在實際使用中,您可能根本不會使用它,但現在在phpunit中,您可以正確使用以下內容。

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

您必須創建自己的存根。你不能只調用phpunit方法getMockObject(),因爲它會直接擴展哈希元素,而常規哈希元素在其構造函數中做'邪惡'的東西。

使用這種方法,您甚至不需要連接到數據庫來測試您的表單!我花了一段時間纔想到這一點。

如果需要,可以將setHashElement()方法(以及變量和get方法)推送到某個FormAbstract基類中。

請記住,在phpunit中,您必須在表單構造過程中傳遞哈希元素。如果你不知道,你的init()方法會在你的存根散列可以用set方法設置之前被調用,並且你最終會使用常規的散列元素。你會知道你正在使用常規的哈希元素,因爲如果你沒有連接到數據庫,你可能會得到一些會話錯誤。

讓我知道如果你覺得這有幫助或如果你使用它。

1

解決方案ZF2是建立在測試您的形式,並從您的CSRF表單元素中獲得價值:

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