代码之家  ›  专栏  ›  技术社区  ›  joedevon

如何对包含CSRF表单元素的Zend_Form进行单元测试?

  •  6
  • joedevon  · 技术社区  · 17 年前

    delicioused them all 但没有人提到这一点。

    5 回复  |  直到 17 年前
        1
  •  9
  •   Luke 10X    16 年前

    每次渲染表单时都会生成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');
    
        // ...
    }
    
        2
  •  3
  •   Jani Hartikainen    17 年前

    正确的哈希存储在会话中,哈希表单元素有一个Zend_session_Namespace实例,其中包含哈希的命名空间。

    要对元素进行单元测试,您可以将元素中的Zend_Session_Namespace实例(使用setSession)替换为您自己创建的包含正确哈希值的实例(哈希值存储在键“hash”中)

    有关更多示例,您可能可以查看Zend_Form_Element_Hash类的Zend Framework单元测试。我想他们也必须处理这个问题。

        3
  •  1
  •   lo_fye    17 年前

    我在我的Apachevhost文件中设置了一个环境变量,它告诉代码它运行在哪个服务器上: 开发、分期或生产

    vhost文件的行是:

    SetEnv SITE_ENV "dev" 
    

    然后,我只是让我的表单对适当的环境做出反应:

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

    我对很多东西都使用同样的技术。例如,如果它是dev,我会将所有发出的电子邮件重定向给我,等等。

        4
  •  1
  •   ByteNudger    13 年前

    我回答了一个与这个类似的问题。我也把我的答案放在这里,以防将来对任何人有帮助。

    我最近发现了一种用哈希元素测试表单的好方法。这将使用一个模拟对象来删除哈希元素,你不必担心它。你甚至不必这样做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
        }
    }
    

    set方法实际上只是用于测试目的。在实际使用中,您可能根本不会使用它,但现在在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基类。

    init() 在使用set方法设置存根哈希值之前,方法将被调用,最终将使用常规哈希元素。你会知道你正在使用常规哈希元素,因为如果你没有连接到数据库,你可能会遇到一些会话错误。

    如果你觉得这有用,或者你使用它,请告诉我。

        5
  •  1
  •   Alex Joe    11 年前

    ZF2的解决方案是在测试中创建表单,并从csrf表单元素中获取值:

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